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