snej@0: // snej@0: // MYCertificate.m snej@0: // MYCrypto snej@0: // snej@0: // Created by Jens Alfke on 3/26/09. snej@0: // Copyright 2009 Jens Alfke. All rights reserved. snej@0: // snej@0: snej@0: #import "MYCertificate.h" snej@0: #import "MYCrypto_Private.h" snej@8: #import "MYDigest.h" snej@8: #import "MYErrorUtils.h" snej@0: snej@2: #if !MYCRYPTO_USE_IPHONE_API snej@0: snej@0: snej@0: @implementation MYCertificate snej@0: snej@0: snej@0: /** Creates a MYCertificate object for an existing Keychain certificate reference. */ snej@0: - (id) initWithCertificateRef: (SecCertificateRef)certificateRef { snej@0: self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef]; snej@0: if (self) { snej@0: _certificateRef = certificateRef; // superclass has already CFRetained it snej@0: } snej@0: return self; snej@0: } snej@0: snej@8: + (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef { snej@8: return [[[self alloc] initWithCertificateRef: certificateRef] autorelease]; snej@8: } snej@8: snej@0: /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */ snej@0: - (id) initWithCertificateData: (NSData*)data snej@0: type: (CSSM_CERT_TYPE) type snej@0: encoding: (CSSM_CERT_ENCODING) encoding snej@0: { snej@0: Assert(data); snej@0: CSSM_DATA cssmData = {.Data=(void*)data.bytes, .Length=data.length}; snej@0: SecCertificateRef certificateRef = NULL; snej@0: if (!check(SecCertificateCreateFromData(&cssmData, type, encoding, &certificateRef), snej@0: @"SecCertificateCreateFromData")) { snej@0: [self release]; snej@0: return nil; snej@0: } snej@0: self = [self initWithCertificateRef: certificateRef]; snej@0: CFRelease(certificateRef); snej@0: return self; snej@0: } snej@0: snej@0: - (id) initWithCertificateData: (NSData*)data { snej@0: return [self initWithCertificateData: data snej@0: type: CSSM_CERT_X_509v3 snej@0: encoding: CSSM_CERT_ENCODING_BER]; snej@0: } snej@0: snej@8: snej@8: - (NSString*) description { snej@8: return $sprintf(@"%@[%@ %@/%p]", snej@8: [self class], snej@8: self.commonName, snej@8: self.certificateData.my_SHA1Digest.abbreviatedHexString, snej@8: _certificateRef); snej@8: } snej@8: snej@8: snej@8: - (BOOL)isEqualToCertificate:(MYCertificate*)cert { snej@8: return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData]; snej@8: } snej@8: snej@8: snej@0: + (MYCertificate*) preferredCertificateForName: (NSString*)name { snej@0: SecCertificateRef certRef = NULL; snej@0: if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef), snej@0: @"SecCertificateCopyPreference")) snej@0: return nil; snej@0: return [[[MYCertificate alloc] initWithCertificateRef: certRef] autorelease]; snej@0: } snej@0: snej@0: - (BOOL) setPreferredCertificateForName: (NSString*)name { snej@0: return check(SecCertificateSetPreference(_certificateRef, (CFStringRef)name, 0, NULL), snej@0: @"SecCertificateSetPreference"); snej@0: } snej@0: snej@8: snej@0: @synthesize certificateRef=_certificateRef; snej@0: snej@0: - (NSData*) certificateData { snej@0: CSSM_DATA cssmData; snej@0: if (!check(SecCertificateGetData(_certificateRef, &cssmData), snej@0: @"SecCertificateGetData")) snej@0: return nil; snej@0: return [NSData dataWithBytes: cssmData.Data length: cssmData.Length]; snej@0: } snej@0: snej@0: - (MYPublicKey*) publicKey { snej@0: SecKeyRef keyRef = NULL; snej@0: if (!check(SecCertificateCopyPublicKey(_certificateRef, &keyRef), snej@0: @"SecCertificateCopyPublicKey") || !keyRef) snej@0: return nil; snej@0: MYPublicKey *key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease]; snej@0: CFRelease(keyRef); snej@0: return key; snej@0: } snej@0: snej@0: - (NSString*) commonName { snej@0: CFStringRef name = NULL; snej@0: if (!check(SecCertificateCopyCommonName(_certificateRef, &name), snej@0: @"SecCertificateCopyCommonName") || !name) snej@0: return nil; snej@0: return [(id)CFMakeCollectable(name) autorelease]; snej@0: } snej@0: snej@0: - (NSArray*) emailAddresses { snej@0: CFArrayRef addrs = NULL; snej@0: if (!check(SecCertificateCopyEmailAddresses(_certificateRef, &addrs), snej@0: @"SecCertificateCopyEmailAddresses") || !addrs) snej@0: return nil; snej@0: return [(id)CFMakeCollectable(addrs) autorelease]; snej@0: } snej@0: snej@0: snej@8: #pragma mark - snej@8: #pragma mark TRUST/POLICY STUFF: snej@8: snej@8: snej@8: + (SecPolicyRef) policyForOID: (CSSM_OID) policyOID { snej@8: SecPolicySearchRef search; snej@8: if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search), snej@8: @"SecPolicySearchCreate")) snej@8: return nil; snej@8: SecPolicyRef policy = NULL; snej@8: if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext")) snej@8: policy = NULL; snej@8: CFRelease(search); snej@8: return policy; snej@8: } snej@8: snej@8: + (SecPolicyRef) X509Policy { snej@8: static SecPolicyRef sX509Policy = NULL; snej@8: if (!sX509Policy) snej@8: sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC]; snej@8: return sX509Policy; snej@8: } snej@8: snej@8: + (SecPolicyRef) SSLPolicy { snej@8: static SecPolicyRef sSSLPolicy = NULL; snej@8: if (!sSSLPolicy) snej@8: sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL]; snej@8: return sSSLPolicy; snej@8: } snej@8: snej@8: + (SecPolicyRef) SMIMEPolicy { snej@8: static SecPolicyRef sSMIMEPolicy = NULL; snej@8: if (!sSMIMEPolicy) snej@8: sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME]; snej@8: return sSMIMEPolicy; snej@8: } snej@8: snej@8: snej@8: - (CSSM_CERT_TYPE) certificateType { snej@8: CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN; snej@8: if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType")) snej@8: type = CSSM_CERT_UNKNOWN; snej@8: return type; snej@8: } snej@8: snej@8: - (NSArray*) trustSettings { snej@8: CFArrayRef settings = NULL; snej@8: OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser, snej@8: &settings); snej@8: if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings) snej@8: return nil; snej@8: return [(id)CFMakeCollectable(settings) autorelease]; snej@8: } snej@8: snej@8: snej@8: - (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting snej@8: { snej@8: if (trustSetting == kSecTrustResultProceed) { snej@8: return check(SecTrustSettingsSetTrustSettings(_certificateRef, snej@8: kSecTrustSettingsDomainUser, nil), snej@8: @"SecTrustSettingsSetTrustSettings"); snej@8: } else if (trustSetting == kSecTrustResultDeny) { snej@8: OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef, snej@8: kSecTrustSettingsDomainUser); snej@8: return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings"); snej@8: } else snej@8: return paramErr; snej@8: } snej@8: snej@8: snej@0: @end snej@0: snej@0: snej@8: NSString* MYPolicyGetName( SecPolicyRef policy ) { snej@8: if (!policy) snej@8: return @"(null)"; snej@8: CSSM_OID oid = {}; snej@8: SecPolicyGetOID(policy, &oid); snej@8: return $sprintf(@"SecPolicy[%@]", OIDAsString(oid)); snej@8: } snej@8: snej@8: NSString* MYTrustResultDescribe( SecTrustResultType result ) { snej@8: static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = { snej@8: @"Invalid", snej@8: @"Proceed", snej@8: @"Confirm", snej@8: @"Deny", snej@8: @"Unspecified", snej@8: @"RecoverableTrustFailure", snej@8: @"FatalTrustFailure", snej@8: @"OtherError" snej@8: }; snej@8: if (result>=0 && result <=kSecTrustResultOtherError) snej@8: return kTrustResultNames[result]; snej@8: else snej@8: return $sprintf(@"(Unknown trust result %i)", result); snej@8: } snej@8: snej@8: snej@8: NSString* MYTrustDescribe( SecTrustRef trust ) { snej@8: SecTrustResultType result; snej@8: CFArrayRef certChain=NULL; snej@8: CSSM_TP_APPLE_EVIDENCE_INFO* statusChain; snej@8: OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain); snej@8: NSString *desc; snej@8: if (err) snej@8: desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err)); snej@8: else snej@8: desc = $sprintf(@"SecTrust[%@, %u in chain]", snej@8: MYTrustResultDescribe(result), snej@8: CFArrayGetCount(certChain)); snej@8: if (certChain) CFRelease(certChain); snej@8: return desc; snej@8: } snej@8: snej@8: snej@8: snej@8: TestCase(Trust) { snej@8: Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy])); snej@8: Log(@" SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy])); snej@8: Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy])); snej@8: for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) { snej@8: NSArray *settings = cert.trustSettings; snej@8: if (settings) snej@8: Log(@"---- %@ = %@", cert, settings); snej@8: } snej@8: } snej@8: snej@8: snej@2: #endif !MYCRYPTO_USE_IPHONE_API