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