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 |
*/
|