Fixed iPhone OS build. (issue 3)
5 // Created by Jens Alfke on 3/26/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYCertificate.h"
10 #import "MYCrypto_Private.h"
11 #import "MYIdentity.h"
13 #import "MYCertificateInfo.h"
14 #import "MYErrorUtils.h"
17 @interface MYCertificate ()
23 @implementation MYCertificate
26 /** Creates a MYCertificate object for an existing Keychain certificate reference. */
27 - (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
28 self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
30 _certificateRef = certificateRef; // superclass has already CFRetained it
31 if (![self _verify]) {
32 Log(@"Self-signed cert failed signature verification (%@)", self);
40 + (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
41 return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
44 /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
45 - (id) initWithCertificateData: (NSData*)data
46 #if !MYCRYPTO_USE_IPHONE_API
47 type: (CSSM_CERT_TYPE) type
48 encoding: (CSSM_CERT_ENCODING) encoding
52 SecCertificateRef certificateRef = NULL;
53 #if MYCRYPTO_USE_IPHONE_API
54 certificateRef = SecCertificateCreateWithData(NULL, (CFDataRef)data);
56 CSSM_DATA cssmData = {.Data=(void*)data.bytes, .Length=data.length};
57 if (!check(SecCertificateCreateFromData(&cssmData, type, encoding, &certificateRef),
58 @"SecCertificateCreateFromData"))
59 certificateRef = NULL;
61 if (!certificateRef) {
65 self = [self initWithCertificateRef: certificateRef];
66 CFRelease(certificateRef);
68 if (![self _verify]) {
69 Log(@"Self-signed cert failed signature verification (%@)", self);
77 #if !MYCRYPTO_USE_IPHONE_API
78 - (id) initWithCertificateData: (NSData*)data {
79 return [self initWithCertificateData: data
80 type: CSSM_CERT_X_509v3
81 encoding: CSSM_CERT_ENCODING_BER];
93 - (NSString*) description {
94 return $sprintf(@"%@[%@ %@/%p]",
97 self.certificateData.my_SHA1Digest.abbreviatedHexString,
102 - (BOOL)isEqualToCertificate:(MYCertificate*)cert {
103 return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
107 #if !TARGET_OS_IPHONE
108 + (MYCertificate*) preferredCertificateForName: (NSString*)name {
109 SecCertificateRef certRef = NULL;
110 if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
111 @"SecCertificateCopyPreference"))
113 return [[[MYCertificate alloc] initWithCertificateRef: certRef] autorelease];
116 - (BOOL) setPreferredCertificateForName: (NSString*)name {
117 return check(SecCertificateSetPreference(_certificateRef, (CFStringRef)name, 0, NULL),
118 @"SecCertificateSetPreference");
120 #endif TARGET_OS_IPHONE
123 @synthesize certificateRef=_certificateRef;
125 - (NSData*) certificateData {
126 #if MYCRYPTO_USE_IPHONE_API
127 CFDataRef data = SecCertificateCopyData(_certificateRef);
128 return data ?[(id)CFMakeCollectable(data) autorelease] :nil;
131 if (!check(SecCertificateGetData(_certificateRef, &cssmData),
132 @"SecCertificateGetData"))
134 return [NSData dataWithBytes: cssmData.Data length: cssmData.Length];
138 - (MYPublicKey*) publicKey {
139 SecKeyRef keyRef = NULL;
140 #if MYCRYPTO_USE_IPHONE_API
141 SecTrustRef trust = NULL;
142 SecPolicyRef policy = SecPolicyCreateBasicX509();
143 OSStatus err = SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef),
147 if (!check(err,@"SecTrustCreateWithCertificates"))
149 SecTrustResultType result;
150 if (!check(SecTrustEvaluate(trust, &result), @"SecTrustEvaluate")) {
154 keyRef = SecTrustCopyPublicKey(trust);
157 if (!check(SecCertificateCopyPublicKey(_certificateRef, &keyRef),
158 @"SecCertificateCopyPublicKey") || !keyRef)
163 MYPublicKey *key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease];
165 #if MYCRYPTO_USE_IPHONE_API
166 key.certificate = self;
167 key.isPersistent = NO;
172 - (MYSHA1Digest*) publicKeyDigest {
173 return self.publicKey.publicKeyDigest;
176 - (MYIdentity*) identity {
177 return [[[MYIdentity alloc] initWithCertificateRef: _certificateRef] autorelease];
180 - (MYCertificateInfo*) info {
183 _info = [[MYCertificateInfo alloc] initWithCertificateData: self.certificateData
186 Warn(@"Couldn't parse certificate %@: %@", self, error);
191 - (NSString*) commonName {
192 CFStringRef name = NULL;
193 #if MYCRYPTO_USE_IPHONE_API
194 name = SecCertificateCopySubjectSummary(_certificateRef);
196 if (!check(SecCertificateCopyCommonName(_certificateRef, &name),
197 @"SecCertificateCopyCommonName"))
200 return name ?[NSMakeCollectable(name) autorelease] :nil;
203 - (NSArray*) emailAddresses {
204 #if MYCRYPTO_USE_IPHONE_API
205 NSString *email = self.info.subject.emailAddress;
206 return email ?$array(email) :nil;
208 CFArrayRef addrs = NULL;
209 if (!check(SecCertificateCopyEmailAddresses(_certificateRef, &addrs),
210 @"SecCertificateCopyEmailAddresses") || !addrs)
212 return [(id)CFMakeCollectable(addrs) autorelease];
218 #pragma mark TRUST/POLICY STUFF:
222 // If the cert is self-signed, verify its signature. Apple's frameworks don't do this,
223 // even the SecTrust API; if the signature doesn't verify, they just assume it could be
224 // signed by a different cert. Seems like a bad decision to me, so I'll add the check:
225 MYCertificateInfo *info = self.info;
226 return !info.isRoot || [info verifySignatureWithKey: self.publicKey];
230 - (SecTrustResultType) evaluateTrustWithPolicy: (SecPolicyRef)policy {
232 if (!check(SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef), policy, &trust),
233 @"SecTrustCreateWithCertificates"))
234 return kSecTrustResultOtherError;
235 SecTrustResultType result;
236 if (!check(SecTrustEvaluate(trust, &result), @"SecTrustEvaluate"))
237 result = kSecTrustResultOtherError;
239 #if !MYCRYPTO_USE_IPHONE_API
240 // This is just to log details:
241 CSSM_TP_APPLE_EVIDENCE_INFO *status;
242 CFArrayRef certChain;
243 if (check(SecTrustGetResult(trust, &result, &certChain, &status), @"SecTrustGetResult")) {
244 Log(@"evaluateTrust: result=%@, bits=%X, certChain=%@", MYTrustResultDescribe(result),status->StatusBits, certChain);
245 for (unsigned i=0; i<status->NumStatusCodes; i++)
246 Log(@" #%i: %X", i, status->StatusCodes[i]);
247 CFRelease(certChain);
255 - (SecTrustResultType) evaluateTrust {
256 return [self evaluateTrustWithPolicy: [[self class] X509Policy]];
260 #if !MYCRYPTO_USE_IPHONE_API
261 + (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
262 SecPolicySearchRef search;
263 if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
264 @"SecPolicySearchCreate"))
266 SecPolicyRef policy = NULL;
267 if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext"))
274 + (SecPolicyRef) X509Policy {
275 static SecPolicyRef sX509Policy = NULL;
277 #if MYCRYPTO_USE_IPHONE_API
278 sX509Policy = SecPolicyCreateBasicX509();
280 sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
286 + (SecPolicyRef) SSLPolicy {
287 static SecPolicyRef sSSLPolicy = NULL;
289 #if MYCRYPTO_USE_IPHONE_API
290 sSSLPolicy = SecPolicyCreateSSL(NO,NULL);
292 sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
298 #if !TARGET_OS_IPHONE
299 + (SecPolicyRef) SMIMEPolicy {
300 static SecPolicyRef sSMIMEPolicy = NULL;
302 sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
307 - (CSSM_CERT_TYPE) certificateType {
308 CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
309 if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
310 type = CSSM_CERT_UNKNOWN;
314 - (NSArray*) trustSettings {
315 CFArrayRef settings = NULL;
316 OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser,
318 if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings)
320 return [(id)CFMakeCollectable(settings) autorelease];
324 - (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting
326 if (trustSetting == kSecTrustResultProceed) {
327 return check(SecTrustSettingsSetTrustSettings(_certificateRef,
328 kSecTrustSettingsDomainUser, nil),
329 @"SecTrustSettingsSetTrustSettings");
330 } else if (trustSetting == kSecTrustResultDeny) {
331 OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef,
332 kSecTrustSettingsDomainUser);
333 return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings");
343 NSString* MYTrustResultDescribe( SecTrustResultType result ) {
344 static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
350 @"RecoverableTrustFailure",
351 @"FatalTrustFailure",
354 if (result>=0 && result <=kSecTrustResultOtherError)
355 return kTrustResultNames[result];
357 return $sprintf(@"(Unknown trust result %i)", result);
361 #if !TARGET_OS_IPHONE
362 NSString* MYPolicyGetName( SecPolicyRef policy ) {
366 SecPolicyGetOID(policy, &oid);
367 return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
370 NSString* MYTrustDescribe( SecTrustRef trust ) {
371 SecTrustResultType result;
372 CFArrayRef certChain=NULL;
373 CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
374 OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain);
377 desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err));
379 desc = $sprintf(@"SecTrust[%@, %u in chain]",
380 MYTrustResultDescribe(result),
381 CFArrayGetCount(certChain));
382 if (certChain) CFRelease(certChain);
387 // Taken from Keychain.framework
388 NSString* OIDAsString(const CSSM_OID oid) {
389 if ((NULL == oid.Data) || (0 >= oid.Length)) {
392 NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
395 for (i = 0; i < oid.Length; ++i) {
396 [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
405 #if !TARGET_OS_IPHONE
407 Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
408 Log(@" SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
409 Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
410 for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
411 NSArray *settings = cert.trustSettings;
413 Log(@"---- %@ = %@", cert, settings);
421 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
423 Redistribution and use in source and binary forms, with or without modification, are permitted
424 provided that the following conditions are met:
426 * Redistributions of source code must retain the above copyright notice, this list of conditions
427 and the following disclaimer.
428 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
429 and the following disclaimer in the documentation and/or other materials provided with the
432 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
433 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
434 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
435 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
436 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
437 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
438 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
439 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.