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
|