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