MYCertificate.m
author Jens Alfke <jens@mooseyard.com>
Sun Apr 19 22:05:51 2009 -0700 (2009-04-19)
changeset 15 2ac5704e229f
parent 12 e4c971be4079
child 16 c409dbc4f068
permissions -rw-r--r--
Added tag v0.3 for changeset 3af1d1c0ceb5
     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 // Taken from Keychain.framework
   240 NSString* OIDAsString(const CSSM_OID oid) {
   241     if ((NULL == oid.Data) || (0 >= oid.Length)) {
   242         return nil;
   243     } else {
   244         NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
   245         unsigned int i;
   246         
   247         for (i = 0; i < oid.Length; ++i) {
   248             [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
   249         }
   250         
   251         return result;
   252     }
   253 }
   254 
   255 
   256 
   257 TestCase(Trust) {
   258     Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
   259     Log(@"  SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
   260     Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
   261     for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
   262         NSArray *settings = cert.trustSettings;
   263         if (settings)
   264             Log(@"---- %@ = %@", cert, settings);
   265     }
   266 }
   267 
   268 
   269 #endif !MYCRYPTO_USE_IPHONE_API
   270 
   271 
   272 
   273 /*
   274  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   275  
   276  Redistribution and use in source and binary forms, with or without modification, are permitted
   277  provided that the following conditions are met:
   278  
   279  * Redistributions of source code must retain the above copyright notice, this list of conditions
   280  and the following disclaimer.
   281  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   282  and the following disclaimer in the documentation and/or other materials provided with the
   283  distribution.
   284  
   285  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   286  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   287  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   288  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   289  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   290   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   291  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   292  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   293  */