MYCertificate.m
author Jens Alfke <jens@mooseyard.com>
Sun Jun 07 21:53:56 2009 -0700 (2009-06-07)
changeset 23 39fec79de6e8
parent 16 c409dbc4f068
child 24 6856e071d25a
permissions -rw-r--r--
A snapshot taken during the long, agonizing crawl toward getting everything running on iPhone.
     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 "MYIdentity.h"
    12 #import "MYDigest.h"
    13 #import "MYCertificateInfo.h"
    14 #import "MYErrorUtils.h"
    15 
    16 #if !MYCRYPTO_USE_IPHONE_API
    17 
    18 
    19 @implementation MYCertificate
    20 
    21 
    22 /** Creates a MYCertificate object for an existing Keychain certificate reference. */
    23 - (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
    24     self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
    25     if (self) {
    26         _certificateRef = certificateRef;     // superclass has already CFRetained it
    27     }
    28     return self;
    29 }
    30 
    31 + (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
    32     return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
    33 }
    34 
    35 /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
    36 - (id) initWithCertificateData: (NSData*)data
    37                           type: (CSSM_CERT_TYPE) type
    38                       encoding: (CSSM_CERT_ENCODING) encoding
    39 {
    40     Assert(data);
    41     CSSM_DATA cssmData = {.Data=(void*)data.bytes, .Length=data.length};
    42     SecCertificateRef certificateRef = NULL;
    43     if (!check(SecCertificateCreateFromData(&cssmData, type, encoding, &certificateRef),
    44         @"SecCertificateCreateFromData")) {
    45         [self release];
    46         return nil;
    47     }
    48     self = [self initWithCertificateRef: certificateRef];
    49     CFRelease(certificateRef);
    50     return self;
    51 }
    52 
    53 - (id) initWithCertificateData: (NSData*)data {
    54     return [self initWithCertificateData: data 
    55                                     type: CSSM_CERT_X_509v3 
    56                                 encoding: CSSM_CERT_ENCODING_BER];
    57 }
    58 
    59 - (void) dealloc
    60 {
    61     [_info release];
    62     [super dealloc];
    63 }
    64 
    65 
    66 
    67 - (NSString*) description {
    68     return $sprintf(@"%@[%@ %@/%p]", 
    69                     [self class],
    70                     self.commonName,
    71                     self.certificateData.my_SHA1Digest.abbreviatedHexString,
    72                     _certificateRef);
    73 }
    74 
    75 
    76 - (BOOL)isEqualToCertificate:(MYCertificate*)cert {
    77     return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
    78 }
    79 
    80 
    81 + (MYCertificate*) preferredCertificateForName: (NSString*)name {
    82     SecCertificateRef certRef = NULL;
    83     if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
    84                @"SecCertificateCopyPreference"))
    85         return nil;
    86     return [[[MYCertificate alloc] initWithCertificateRef: certRef] autorelease];
    87 }
    88 
    89 - (BOOL) setPreferredCertificateForName: (NSString*)name {
    90     return check(SecCertificateSetPreference(_certificateRef, (CFStringRef)name, 0, NULL),
    91                  @"SecCertificateSetPreference");
    92 }
    93 
    94 
    95 @synthesize certificateRef=_certificateRef;
    96 
    97 - (NSData*) certificateData {
    98     CSSM_DATA cssmData;
    99     if (!check(SecCertificateGetData(_certificateRef, &cssmData),
   100                @"SecCertificateGetData"))
   101         return nil;
   102     return [NSData dataWithBytes: cssmData.Data length: cssmData.Length];
   103 }
   104 
   105 - (MYPublicKey*) publicKey {
   106     SecKeyRef keyRef = NULL;
   107     if (!check(SecCertificateCopyPublicKey(_certificateRef, &keyRef),
   108                @"SecCertificateCopyPublicKey") || !keyRef)
   109         return nil;
   110     MYPublicKey *key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease];
   111     CFRelease(keyRef);
   112     return key;
   113 }
   114 
   115 - (MYIdentity*) identity {
   116     return [[[MYIdentity alloc] initWithCertificateRef: _certificateRef] autorelease];
   117 }
   118 
   119 - (MYCertificateInfo*) info {
   120     if (!_info) {
   121         NSError *error;
   122         _info = [[MYCertificateInfo alloc] initWithCertificateData: self.certificateData
   123                                                              error: &error];
   124         if (!_info)
   125             Warn(@"Couldn't parse certificate %@: %@", self, error);
   126     }
   127     return _info;
   128 }
   129 
   130 - (NSString*) commonName {
   131     CFStringRef name = NULL;
   132     if (!check(SecCertificateCopyCommonName(_certificateRef, &name),
   133                @"SecCertificateCopyCommonName") || !name)
   134         return nil;
   135     return [(id)CFMakeCollectable(name) autorelease];
   136 }
   137 
   138 - (NSArray*) emailAddresses {
   139     CFArrayRef addrs = NULL;
   140     if (!check(SecCertificateCopyEmailAddresses(_certificateRef, &addrs),
   141                @"SecCertificateCopyEmailAddresses") || !addrs)
   142         return nil;
   143     return [(id)CFMakeCollectable(addrs) autorelease];
   144 }
   145 
   146 
   147 #pragma mark -
   148 #pragma mark TRUST/POLICY STUFF:
   149 
   150 
   151 + (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
   152     SecPolicySearchRef search;
   153     if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
   154            @"SecPolicySearchCreate"))
   155         return nil;
   156     SecPolicyRef policy = NULL;
   157     if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext"))
   158         policy = NULL;
   159     CFRelease(search);
   160     return policy;
   161 }
   162 
   163 + (SecPolicyRef) X509Policy {
   164     static SecPolicyRef sX509Policy = NULL;
   165     if (!sX509Policy)
   166         sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
   167     return sX509Policy;
   168 }
   169 
   170 + (SecPolicyRef) SSLPolicy {
   171     static SecPolicyRef sSSLPolicy = NULL;
   172     if (!sSSLPolicy)
   173         sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
   174     return sSSLPolicy;
   175 }
   176 
   177 + (SecPolicyRef) SMIMEPolicy {
   178     static SecPolicyRef sSMIMEPolicy = NULL;
   179     if (!sSMIMEPolicy)
   180         sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
   181     return sSMIMEPolicy;
   182 }
   183 
   184 
   185 - (CSSM_CERT_TYPE) certificateType {
   186     CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
   187     if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
   188         type = CSSM_CERT_UNKNOWN;
   189     return type;
   190 }
   191 
   192 - (NSArray*) trustSettings {
   193     CFArrayRef settings = NULL;
   194     OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser, 
   195                                                      &settings);
   196     if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings)
   197         return nil;
   198     return [(id)CFMakeCollectable(settings) autorelease];
   199 }
   200         
   201 
   202 - (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting
   203 {
   204     if (trustSetting == kSecTrustResultProceed) {
   205         return check(SecTrustSettingsSetTrustSettings(_certificateRef, 
   206                                                       kSecTrustSettingsDomainUser, nil),
   207                      @"SecTrustSettingsSetTrustSettings");
   208     } else if (trustSetting == kSecTrustResultDeny) {
   209         OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef, 
   210                                                            kSecTrustSettingsDomainUser);
   211         return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings");
   212     } else
   213         return paramErr;
   214 }
   215 
   216 
   217 @end
   218 
   219 
   220 NSString* MYPolicyGetName( SecPolicyRef policy ) {
   221     if (!policy)
   222         return @"(null)";
   223     CSSM_OID oid = {};
   224     SecPolicyGetOID(policy, &oid);
   225     return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
   226 }
   227 
   228 NSString* MYTrustResultDescribe( SecTrustResultType result ) {
   229     static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
   230         @"Invalid",
   231         @"Proceed",
   232         @"Confirm",
   233         @"Deny",
   234         @"Unspecified",
   235         @"RecoverableTrustFailure",
   236         @"FatalTrustFailure",
   237         @"OtherError"
   238     };
   239     if (result>=0 && result <=kSecTrustResultOtherError)
   240         return kTrustResultNames[result];
   241     else
   242         return $sprintf(@"(Unknown trust result %i)", result);
   243 }
   244 
   245 
   246 NSString* MYTrustDescribe( SecTrustRef trust ) {
   247     SecTrustResultType result;
   248     CFArrayRef certChain=NULL;
   249     CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
   250     OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain);
   251     NSString *desc;
   252     if (err)
   253         desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err));
   254     else
   255         desc = $sprintf(@"SecTrust[%@, %u in chain]", 
   256                         MYTrustResultDescribe(result),
   257                         CFArrayGetCount(certChain));
   258     if (certChain) CFRelease(certChain);
   259     return desc;
   260 }
   261 
   262 
   263 // Taken from Keychain.framework
   264 NSString* OIDAsString(const CSSM_OID oid) {
   265     if ((NULL == oid.Data) || (0 >= oid.Length)) {
   266         return nil;
   267     } else {
   268         NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
   269         unsigned int i;
   270         
   271         for (i = 0; i < oid.Length; ++i) {
   272             [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
   273         }
   274         
   275         return result;
   276     }
   277 }
   278 
   279 
   280 
   281 TestCase(Trust) {
   282     Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
   283     Log(@"  SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
   284     Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
   285     for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
   286         NSArray *settings = cert.trustSettings;
   287         if (settings)
   288             Log(@"---- %@ = %@", cert, settings);
   289     }
   290 }
   291 
   292 
   293 #endif !MYCRYPTO_USE_IPHONE_API
   294 
   295 
   296 
   297 /*
   298  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   299  
   300  Redistribution and use in source and binary forms, with or without modification, are permitted
   301  provided that the following conditions are met:
   302  
   303  * Redistributions of source code must retain the above copyright notice, this list of conditions
   304  and the following disclaimer.
   305  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   306  and the following disclaimer in the documentation and/or other materials provided with the
   307  distribution.
   308  
   309  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   310  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   311  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   312  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   313  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   314   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   315  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   316  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   317  */