Changed the X.509 version number in generated certs from 1 to 3, so that SecCertificateCreateFromData on iPhone will accept them. :-/
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 @implementation MYCertificate
20 /** Creates a MYCertificate object for an existing Keychain certificate reference. */
21 - (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
22 self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
24 _certificateRef = certificateRef; // superclass has already CFRetained it
29 + (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
30 return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
33 /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
34 - (id) initWithCertificateData: (NSData*)data
35 #if !MYCRYPTO_USE_IPHONE_API
36 type: (CSSM_CERT_TYPE) type
37 encoding: (CSSM_CERT_ENCODING) encoding
41 SecCertificateRef certificateRef = NULL;
42 #if MYCRYPTO_USE_IPHONE_API
43 certificateRef = SecCertificateCreateWithData(NULL, (CFDataRef)data);
45 CSSM_DATA cssmData = {.Data=(void*)data.bytes, .Length=data.length};
46 if (!check(SecCertificateCreateFromData(&cssmData, type, encoding, &certificateRef),
47 @"SecCertificateCreateFromData"))
48 certificateRef = NULL;
50 if (!certificateRef) {
54 self = [self initWithCertificateRef: certificateRef];
55 CFRelease(certificateRef);
57 // If the cert is self-signed, verify its signature. Apple's frameworks don't do this,
58 // even the SecTrust API; if the signature doesn't verify, they just assume it could be
59 // signed by a different cert. Seems like a bad decision to me, so I'll add the check:
60 MYCertificateInfo *info = self.info;
62 Log(@"Verifying self-signed certificate %@ ...", self);
63 if (![info verifySignatureWithKey: self.publicKey]) {
64 Log(@"Self-signed cert failed signature verification (%@)", self);
73 #if !MYCRYPTO_USE_IPHONE_API
74 - (id) initWithCertificateData: (NSData*)data {
75 return [self initWithCertificateData: data
76 type: CSSM_CERT_X_509v3
77 encoding: CSSM_CERT_ENCODING_BER];
88 - (NSString*) description {
89 return $sprintf(@"%@[%@ %@/%p]",
92 self.certificateData.my_SHA1Digest.abbreviatedHexString,
97 - (BOOL)isEqualToCertificate:(MYCertificate*)cert {
98 return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
102 #if !TARGET_OS_IPHONE
103 + (MYCertificate*) preferredCertificateForName: (NSString*)name {
104 SecCertificateRef certRef = NULL;
105 if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
106 @"SecCertificateCopyPreference"))
108 return [[[MYCertificate alloc] initWithCertificateRef: certRef] autorelease];
111 - (BOOL) setPreferredCertificateForName: (NSString*)name {
112 return check(SecCertificateSetPreference(_certificateRef, (CFStringRef)name, 0, NULL),
113 @"SecCertificateSetPreference");
115 #endif TARGET_OS_IPHONE
118 @synthesize certificateRef=_certificateRef;
120 - (NSData*) certificateData {
121 #if MYCRYPTO_USE_IPHONE_API
122 CFDataRef data = SecCertificateCopyData(_certificateRef);
123 return data ?[(id)CFMakeCollectable(data) autorelease] :nil;
126 if (!check(SecCertificateGetData(_certificateRef, &cssmData),
127 @"SecCertificateGetData"))
129 return [NSData dataWithBytes: cssmData.Data length: cssmData.Length];
133 - (MYPublicKey*) publicKey {
134 SecKeyRef keyRef = NULL;
135 #if MYCRYPTO_USE_IPHONE_API
136 SecTrustRef trust = NULL;
137 SecPolicyRef policy = SecPolicyCreateBasicX509();
138 OSStatus err = SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef),
142 if (!check(err,@"SecTrustCreateWithCertificates"))
144 SecTrustResultType result;
145 if (!check(SecTrustEvaluate(trust, &result), @"SecTrustEvaluate")) {
149 keyRef = SecTrustCopyPublicKey(trust);
152 if (!check(SecCertificateCopyPublicKey(_certificateRef, &keyRef),
153 @"SecCertificateCopyPublicKey") || !keyRef)
158 MYPublicKey *key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease];
163 - (MYIdentity*) identity {
164 return [[[MYIdentity alloc] initWithCertificateRef: _certificateRef] autorelease];
167 - (MYCertificateInfo*) info {
170 _info = [[MYCertificateInfo alloc] initWithCertificateData: self.certificateData
173 Warn(@"Couldn't parse certificate %@: %@", self, error);
178 - (NSString*) commonName {
179 CFStringRef name = NULL;
180 #if MYCRYPTO_USE_IPHONE_API
181 name = SecCertificateCopySubjectSummary(_certificateRef);
183 if (!check(SecCertificateCopyCommonName(_certificateRef, &name),
184 @"SecCertificateCopyCommonName"))
187 return name ?[NSMakeCollectable(name) autorelease] :nil;
190 - (NSArray*) emailAddresses {
191 #if MYCRYPTO_USE_IPHONE_API
192 NSString *email = self.info.subject.emailAddress;
193 return email ?$array(email) :nil;
195 CFArrayRef addrs = NULL;
196 if (!check(SecCertificateCopyEmailAddresses(_certificateRef, &addrs),
197 @"SecCertificateCopyEmailAddresses") || !addrs)
199 return [(id)CFMakeCollectable(addrs) autorelease];
205 #pragma mark TRUST/POLICY STUFF:
208 - (SecTrustResultType) evaluateTrustWithPolicy: (SecPolicyRef)policy {
210 if (!check(SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef), policy, &trust),
211 @"SecTrustCreateWithCertificates"))
212 return kSecTrustResultOtherError;
213 SecTrustResultType result;
214 if (!check(SecTrustEvaluate(trust, &result), @"SecTrustEvaluate"))
215 result = kSecTrustResultOtherError;
218 // This is just to log details:
219 CSSM_TP_APPLE_EVIDENCE_INFO *status;
220 CFArrayRef certChain;
221 if (check(SecTrustGetResult(trust, &result, &certChain, &status), @"SecTrustGetResult")) {
222 Log(@"evaluateTrust: result=%@, bits=%X, certChain=%@", MYTrustResultDescribe(result),status->StatusBits, certChain);
223 for (unsigned i=0; i<status->NumStatusCodes; i++)
224 Log(@" #%i: %X", i, status->StatusCodes[i]);
225 CFRelease(certChain);
233 - (SecTrustResultType) evaluateTrust {
234 return [self evaluateTrustWithPolicy: [[self class] X509Policy]];
238 #if !MYCRYPTO_USE_IPHONE_API
239 + (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
240 SecPolicySearchRef search;
241 if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
242 @"SecPolicySearchCreate"))
244 SecPolicyRef policy = NULL;
245 if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext"))
252 + (SecPolicyRef) X509Policy {
253 static SecPolicyRef sX509Policy = NULL;
255 #if MYCRYPTO_USE_IPHONE_API
256 sX509Policy = SecPolicyCreateBasicX509();
258 sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
264 + (SecPolicyRef) SSLPolicy {
265 static SecPolicyRef sSSLPolicy = NULL;
267 #if MYCRYPTO_USE_IPHONE_API
268 sSSLPolicy = SecPolicyCreateSSL(NO,NULL);
270 sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
276 #if !TARGET_OS_IPHONE
277 + (SecPolicyRef) SMIMEPolicy {
278 static SecPolicyRef sSMIMEPolicy = NULL;
280 sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
285 - (CSSM_CERT_TYPE) certificateType {
286 CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
287 if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
288 type = CSSM_CERT_UNKNOWN;
292 - (NSArray*) trustSettings {
293 CFArrayRef settings = NULL;
294 OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser,
296 if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings)
298 return [(id)CFMakeCollectable(settings) autorelease];
302 - (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting
304 if (trustSetting == kSecTrustResultProceed) {
305 return check(SecTrustSettingsSetTrustSettings(_certificateRef,
306 kSecTrustSettingsDomainUser, nil),
307 @"SecTrustSettingsSetTrustSettings");
308 } else if (trustSetting == kSecTrustResultDeny) {
309 OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef,
310 kSecTrustSettingsDomainUser);
311 return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings");
321 NSString* MYTrustResultDescribe( SecTrustResultType result ) {
322 static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
328 @"RecoverableTrustFailure",
329 @"FatalTrustFailure",
332 if (result>=0 && result <=kSecTrustResultOtherError)
333 return kTrustResultNames[result];
335 return $sprintf(@"(Unknown trust result %i)", result);
339 #if !TARGET_OS_IPHONE
340 NSString* MYPolicyGetName( SecPolicyRef policy ) {
344 SecPolicyGetOID(policy, &oid);
345 return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
348 NSString* MYTrustDescribe( SecTrustRef trust ) {
349 SecTrustResultType result;
350 CFArrayRef certChain=NULL;
351 CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
352 OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain);
355 desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err));
357 desc = $sprintf(@"SecTrust[%@, %u in chain]",
358 MYTrustResultDescribe(result),
359 CFArrayGetCount(certChain));
360 if (certChain) CFRelease(certChain);
365 // Taken from Keychain.framework
366 NSString* OIDAsString(const CSSM_OID oid) {
367 if ((NULL == oid.Data) || (0 >= oid.Length)) {
370 NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
373 for (i = 0; i < oid.Length; ++i) {
374 [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
383 #if !TARGET_OS_IPHONE
385 Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
386 Log(@" SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
387 Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
388 for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
389 NSArray *settings = cert.trustSettings;
391 Log(@"---- %@ = %@", cert, settings);
399 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
401 Redistribution and use in source and binary forms, with or without modification, are permitted
402 provided that the following conditions are met:
404 * Redistributions of source code must retain the above copyright notice, this list of conditions
405 and the following disclaimer.
406 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
407 and the following disclaimer in the documentation and/or other materials provided with the
410 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
411 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
412 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
413 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
414 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
415 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
416 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
417 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.