MYCertificate.m
author snej@snej.local
Tue Apr 14 18:34:52 2009 -0700 (2009-04-14)
changeset 11 3568d5fd4b6a
parent 2 8982b8fada63
child 12 e4c971be4079
permissions -rw-r--r--
* The build process runs Doxygen only if it's installed (i.e. on the shell search path).
* Added instructions to the README on setting up a named Source Tree for MYUtilities.
* Changed the RSA key size in MYCryptoTest to 2048 and made it a named constant.
     1 //
     2 //  MYCertificate.m
     3 //  MYCrypto
     4 //
     5 //  Created by Jens Alfke on 3/26/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYCertificate.h"
    10 #import "MYCrypto_Private.h"
    11 #import "MYDigest.h"
    12 #import "MYErrorUtils.h"
    13 
    14 #if !MYCRYPTO_USE_IPHONE_API
    15 
    16 
    17 @implementation MYCertificate
    18 
    19 
    20 /** Creates a MYCertificate object for an existing Keychain certificate reference. */
    21 - (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
    22     self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
    23     if (self) {
    24         _certificateRef = certificateRef;     // superclass has already CFRetained it
    25     }
    26     return self;
    27 }
    28 
    29 + (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
    30     return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
    31 }
    32 
    33 /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
    34 - (id) initWithCertificateData: (NSData*)data
    35                           type: (CSSM_CERT_TYPE) type
    36                       encoding: (CSSM_CERT_ENCODING) encoding
    37 {
    38     Assert(data);
    39     CSSM_DATA cssmData = {.Data=(void*)data.bytes, .Length=data.length};
    40     SecCertificateRef certificateRef = NULL;
    41     if (!check(SecCertificateCreateFromData(&cssmData, type, encoding, &certificateRef),
    42         @"SecCertificateCreateFromData")) {
    43         [self release];
    44         return nil;
    45     }
    46     self = [self initWithCertificateRef: certificateRef];
    47     CFRelease(certificateRef);
    48     return self;
    49 }
    50 
    51 - (id) initWithCertificateData: (NSData*)data {
    52     return [self initWithCertificateData: data 
    53                                     type: CSSM_CERT_X_509v3 
    54                                 encoding: CSSM_CERT_ENCODING_BER];
    55 }
    56 
    57 
    58 - (NSString*) description {
    59     return $sprintf(@"%@[%@ %@/%p]", 
    60                     [self class],
    61                     self.commonName,
    62                     self.certificateData.my_SHA1Digest.abbreviatedHexString,
    63                     _certificateRef);
    64 }
    65 
    66 
    67 - (BOOL)isEqualToCertificate:(MYCertificate*)cert {
    68     return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
    69 }
    70 
    71 
    72 + (MYCertificate*) preferredCertificateForName: (NSString*)name {
    73     SecCertificateRef certRef = NULL;
    74     if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
    75                @"SecCertificateCopyPreference"))
    76         return nil;
    77     return [[[MYCertificate alloc] initWithCertificateRef: certRef] autorelease];
    78 }
    79 
    80 - (BOOL) setPreferredCertificateForName: (NSString*)name {
    81     return check(SecCertificateSetPreference(_certificateRef, (CFStringRef)name, 0, NULL),
    82                  @"SecCertificateSetPreference");
    83 }
    84 
    85 
    86 @synthesize certificateRef=_certificateRef;
    87 
    88 - (NSData*) certificateData {
    89     CSSM_DATA cssmData;
    90     if (!check(SecCertificateGetData(_certificateRef, &cssmData),
    91                @"SecCertificateGetData"))
    92         return nil;
    93     return [NSData dataWithBytes: cssmData.Data length: cssmData.Length];
    94 }
    95 
    96 - (MYPublicKey*) publicKey {
    97     SecKeyRef keyRef = NULL;
    98     if (!check(SecCertificateCopyPublicKey(_certificateRef, &keyRef),
    99                @"SecCertificateCopyPublicKey") || !keyRef)
   100         return nil;
   101     MYPublicKey *key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease];
   102     CFRelease(keyRef);
   103     return key;
   104 }
   105 
   106 - (NSString*) commonName {
   107     CFStringRef name = NULL;
   108     if (!check(SecCertificateCopyCommonName(_certificateRef, &name),
   109                @"SecCertificateCopyCommonName") || !name)
   110         return nil;
   111     return [(id)CFMakeCollectable(name) autorelease];
   112 }
   113 
   114 - (NSArray*) emailAddresses {
   115     CFArrayRef addrs = NULL;
   116     if (!check(SecCertificateCopyEmailAddresses(_certificateRef, &addrs),
   117                @"SecCertificateCopyEmailAddresses") || !addrs)
   118         return nil;
   119     return [(id)CFMakeCollectable(addrs) autorelease];
   120 }
   121 
   122 
   123 #pragma mark -
   124 #pragma mark TRUST/POLICY STUFF:
   125 
   126 
   127 + (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
   128     SecPolicySearchRef search;
   129     if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
   130            @"SecPolicySearchCreate"))
   131         return nil;
   132     SecPolicyRef policy = NULL;
   133     if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext"))
   134         policy = NULL;
   135     CFRelease(search);
   136     return policy;
   137 }
   138 
   139 + (SecPolicyRef) X509Policy {
   140     static SecPolicyRef sX509Policy = NULL;
   141     if (!sX509Policy)
   142         sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
   143     return sX509Policy;
   144 }
   145 
   146 + (SecPolicyRef) SSLPolicy {
   147     static SecPolicyRef sSSLPolicy = NULL;
   148     if (!sSSLPolicy)
   149         sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
   150     return sSSLPolicy;
   151 }
   152 
   153 + (SecPolicyRef) SMIMEPolicy {
   154     static SecPolicyRef sSMIMEPolicy = NULL;
   155     if (!sSMIMEPolicy)
   156         sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
   157     return sSMIMEPolicy;
   158 }
   159 
   160 
   161 - (CSSM_CERT_TYPE) certificateType {
   162     CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
   163     if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
   164         type = CSSM_CERT_UNKNOWN;
   165     return type;
   166 }
   167 
   168 - (NSArray*) trustSettings {
   169     CFArrayRef settings = NULL;
   170     OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser, 
   171                                                      &settings);
   172     if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings)
   173         return nil;
   174     return [(id)CFMakeCollectable(settings) autorelease];
   175 }
   176         
   177 
   178 - (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting
   179 {
   180     if (trustSetting == kSecTrustResultProceed) {
   181         return check(SecTrustSettingsSetTrustSettings(_certificateRef, 
   182                                                       kSecTrustSettingsDomainUser, nil),
   183                      @"SecTrustSettingsSetTrustSettings");
   184     } else if (trustSetting == kSecTrustResultDeny) {
   185         OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef, 
   186                                                            kSecTrustSettingsDomainUser);
   187         return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings");
   188     } else
   189         return paramErr;
   190 }
   191 
   192 
   193 @end
   194 
   195 
   196 NSString* MYPolicyGetName( SecPolicyRef policy ) {
   197     if (!policy)
   198         return @"(null)";
   199     CSSM_OID oid = {};
   200     SecPolicyGetOID(policy, &oid);
   201     return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
   202 }
   203 
   204 NSString* MYTrustResultDescribe( SecTrustResultType result ) {
   205     static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
   206         @"Invalid",
   207         @"Proceed",
   208         @"Confirm",
   209         @"Deny",
   210         @"Unspecified",
   211         @"RecoverableTrustFailure",
   212         @"FatalTrustFailure",
   213         @"OtherError"
   214     };
   215     if (result>=0 && result <=kSecTrustResultOtherError)
   216         return kTrustResultNames[result];
   217     else
   218         return $sprintf(@"(Unknown trust result %i)", result);
   219 }
   220 
   221 
   222 NSString* MYTrustDescribe( SecTrustRef trust ) {
   223     SecTrustResultType result;
   224     CFArrayRef certChain=NULL;
   225     CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
   226     OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain);
   227     NSString *desc;
   228     if (err)
   229         desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err));
   230     else
   231         desc = $sprintf(@"SecTrust[%@, %u in chain]", 
   232                         MYTrustResultDescribe(result),
   233                         CFArrayGetCount(certChain));
   234     if (certChain) CFRelease(certChain);
   235     return desc;
   236 }
   237 
   238 
   239 
   240 TestCase(Trust) {
   241     Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
   242     Log(@"  SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
   243     Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
   244     for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
   245         NSArray *settings = cert.trustSettings;
   246         if (settings)
   247             Log(@"---- %@ = %@", cert, settings);
   248     }
   249 }
   250 
   251 
   252 #endif !MYCRYPTO_USE_IPHONE_API