]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2007-2014 Apple Inc. All Rights Reserved. |
427c49bc | 3 | * |
b1ab9ed8 | 4 | * @APPLE_LICENSE_HEADER_START@ |
d8f41ccd | 5 | * |
b1ab9ed8 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
d8f41ccd | 12 | * |
b1ab9ed8 A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
d8f41ccd | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | #include <Security/SecBase.h> | |
25 | #include <Security/SecBasePriv.h> | |
26 | #include <Security/SecItem.h> | |
27 | //#include <Security/SecRSAKey.h> | |
28 | #include <Security/SecCertificate.h> | |
29 | #include <Security/SecIdentity.h> | |
30 | #include <Security/SecIdentityPriv.h> | |
31 | #include <Security/SecPolicy.h> | |
32 | #include <Security/SecTrust.h> | |
33 | #include <Security/SecKeyPriv.h> | |
34 | #include "SecInternal.h" | |
35 | ||
36 | //#include <AssertMacros.h> | |
37 | #include <CommonCrypto/CommonDigest.h> | |
38 | ||
39 | //#include "p12import.h" | |
6b200bc3 | 40 | #include <Security/SecImportExport.h> |
b1ab9ed8 | 41 | |
5c19dc3a A |
42 | const CFStringRef __nonnull kSecImportExportPassphrase = CFSTR("passphrase"); |
43 | const CFStringRef __nonnull kSecImportExportKeychain = CFSTR("keychain"); | |
44 | const CFStringRef __nonnull kSecImportExportAccess = CFSTR("access"); | |
b1ab9ed8 | 45 | |
5c19dc3a A |
46 | const CFStringRef __nonnull kSecImportItemLabel = CFSTR("label"); |
47 | const CFStringRef __nonnull kSecImportItemKeyID = CFSTR("keyid"); | |
48 | const CFStringRef __nonnull kSecImportItemTrust = CFSTR("trust"); | |
49 | const CFStringRef __nonnull kSecImportItemCertChain = CFSTR("chain"); | |
50 | const CFStringRef __nonnull kSecImportItemIdentity = CFSTR("identity"); | |
b1ab9ed8 A |
51 | |
52 | #if 0 | |
53 | static void collect_certs(const void *key, const void *value, void *context) | |
54 | { | |
55 | if (!CFDictionaryContainsKey(value, CFSTR("key"))) { | |
56 | CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert")); | |
57 | if (!cert_bytes) | |
58 | return; | |
427c49bc | 59 | SecCertificateRef cert = |
b1ab9ed8 A |
60 | SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes); |
61 | if (!cert) | |
62 | return; | |
63 | CFMutableArrayRef cert_array = (CFMutableArrayRef)context; | |
64 | CFArrayAppendValue(cert_array, cert); | |
65 | CFRelease(cert); | |
66 | } | |
67 | } | |
68 | ||
69 | typedef struct { | |
70 | CFMutableArrayRef identities; | |
71 | CFArrayRef certs; | |
72 | } build_trust_chains_context; | |
73 | ||
427c49bc | 74 | static void build_trust_chains(const void *key, const void *value, |
b1ab9ed8 A |
75 | void *context) |
76 | { | |
427c49bc | 77 | CFMutableDictionaryRef identity_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, |
b1ab9ed8 A |
78 | 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
79 | SecKeyRef private_key = NULL; | |
80 | SecCertificateRef cert = NULL; | |
81 | SecIdentityRef identity = NULL; | |
82 | SecPolicyRef policy = NULL; | |
83 | CFMutableArrayRef cert_chain = NULL, eval_chain = NULL; | |
84 | SecTrustRef trust = NULL; | |
85 | build_trust_chains_context * a_build_trust_chains_context = (build_trust_chains_context*)context; | |
86 | ||
87 | CFDataRef key_bytes = CFDictionaryGetValue(value, CFSTR("key")); | |
88 | if(!key_bytes) goto out; //require(key_bytes, out); | |
89 | CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert")); | |
90 | if(!cert_bytes) goto out; //require(cert_bytes, out); | |
91 | ||
92 | /* p12import only passes up rsa keys */ | |
93 | //FIXME: needs SecKeyCreateRSAPrivateKey implementation | |
94 | //#if 0 | |
427c49bc | 95 | // private_key = SecKeyCreateRSAPrivateKey(kCFAllocatorDefault, |
b1ab9ed8 A |
96 | // CFDataGetBytePtr(key_bytes), CFDataGetLength(key_bytes), |
97 | // kSecKeyEncodingPkcs1); | |
98 | //#endif | |
99 | if(!private_key) goto out; //require(private_key, out); | |
100 | cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes); | |
101 | if(!cert) goto out; //require(cert, out); | |
102 | identity = SecIdentityCreate(kCFAllocatorDefault, cert, private_key); | |
103 | if(!identity) goto out; //require(identity, out); | |
104 | CFDictionarySetValue(identity_dict, kSecImportItemIdentity, identity); | |
427c49bc | 105 | |
b1ab9ed8 A |
106 | eval_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
107 | if(!eval_chain) goto out; //require(eval_chain, out); | |
108 | CFArrayAppendValue(eval_chain, cert); | |
109 | CFRange all_certs = { 0, CFArrayGetCount(a_build_trust_chains_context->certs) }; | |
110 | CFArrayAppendArray(eval_chain, a_build_trust_chains_context->certs, all_certs); | |
111 | policy = SecPolicyCreateBasicX509(); | |
112 | if(!policy) goto out; //require(policy, out); | |
113 | SecTrustResultType result; | |
114 | SecTrustCreateWithCertificates(eval_chain, policy, &trust); | |
115 | if(!trust) goto out; //require(trust, out); | |
116 | SecTrustEvaluate(trust, &result); | |
117 | CFDictionarySetValue(identity_dict, kSecImportItemTrust, trust); | |
427c49bc | 118 | |
b1ab9ed8 A |
119 | cert_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
120 | if(!cert_chain) goto out; //require(cert_chain, out); | |
121 | CFIndex cert_chain_length = SecTrustGetCertificateCount(trust); | |
122 | int i; | |
123 | for (i = 0; i < cert_chain_length; i++) | |
124 | CFArrayAppendValue(cert_chain, SecTrustGetCertificateAtIndex(trust, i)); | |
125 | CFDictionarySetValue(identity_dict, kSecImportItemCertChain, cert_chain); | |
427c49bc | 126 | |
b1ab9ed8 A |
127 | CFArrayAppendValue(a_build_trust_chains_context->identities, identity_dict); |
128 | out: | |
129 | CFReleaseSafe(identity_dict); | |
130 | CFReleaseSafe(identity); | |
131 | CFReleaseSafe(private_key); | |
132 | CFReleaseSafe(cert); | |
133 | CFReleaseSafe(policy); | |
134 | CFReleaseSafe(cert_chain); | |
135 | CFReleaseSafe(eval_chain); | |
136 | CFReleaseSafe(trust); | |
137 | } | |
138 | #endif // if 0 | |
139 | ||
140 | OSStatus SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options, CFArrayRef *items) | |
141 | { | |
142 | // SecPKCS12Import is implemented on Mac OS X in terms of the existing | |
143 | // SecKeychainItemImport API, which supports importing items into a | |
144 | // specified keychain with initial access control settings for keys. | |
145 | // | |
427c49bc | 146 | OSStatus status = errSecSuccess; |
b1ab9ed8 A |
147 | SecExternalFormat inputFormat = kSecFormatPKCS12; |
148 | SecExternalItemType itemType = kSecItemTypeAggregate; | |
149 | SecItemImportExportFlags flags = 0; /* don't know if it's PEM armoured */ | |
150 | SecKeyImportExportParameters keyParams; /* filled in below... */ | |
151 | SecKeychainRef importKeychain = NULL; | |
152 | SecAccessRef importAccess = NULL; | |
153 | CFStringRef importPassword = NULL; | |
154 | CFArrayRef tmpItems = NULL; /* items returned by SecKeychainItemImport */ | |
155 | CFMutableArrayRef certs = NULL; /* certificates imported by this function */ | |
156 | CFMutableArrayRef identities = NULL; /* items returned by this function */ | |
157 | ||
158 | if (options) { | |
159 | importKeychain = (SecKeychainRef) CFDictionaryGetValue(options, kSecImportExportKeychain); | |
160 | if (importKeychain) | |
161 | CFRetain(importKeychain); | |
162 | importAccess = (SecAccessRef) CFDictionaryGetValue(options, kSecImportExportAccess); | |
163 | if (importAccess) | |
164 | CFRetain(importAccess); | |
165 | importPassword = (CFStringRef) CFDictionaryGetValue(options, kSecImportExportPassphrase); | |
166 | if (importPassword) | |
167 | CFRetain(importPassword); | |
168 | } | |
427c49bc | 169 | |
b1ab9ed8 A |
170 | if (!importKeychain) { |
171 | // SecKeychainItemImport requires a keychain, so use default | |
172 | status = SecKeychainCopyDefault(&importKeychain); | |
173 | } | |
427c49bc | 174 | |
b1ab9ed8 A |
175 | memset(&keyParams, 0, sizeof(SecKeyImportExportParameters)); |
176 | keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; | |
177 | keyParams.passphrase = importPassword; | |
178 | keyParams.accessRef = importAccess; | |
179 | ||
180 | status = SecKeychainItemImport(pkcs12_data, | |
181 | NULL, /* no filename */ | |
182 | &inputFormat, | |
183 | &itemType, | |
427c49bc | 184 | flags, |
b1ab9ed8 A |
185 | &keyParams, |
186 | importKeychain, | |
187 | &tmpItems); | |
427c49bc | 188 | |
b1ab9ed8 A |
189 | // build an array of all non-identity certificates which were imported |
190 | if (!status) { | |
191 | certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
192 | CFIndex i, count = CFArrayGetCount(tmpItems); | |
193 | for (i=0; i<count; i++) { | |
194 | CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i); | |
195 | CFTypeID itemID = CFGetTypeID(anItem); | |
196 | if (itemID == SecCertificateGetTypeID()) { | |
197 | CFArrayAppendValue(certs, anItem); | |
198 | } | |
199 | } | |
200 | } | |
427c49bc | 201 | |
b1ab9ed8 A |
202 | // now build the output items (array of dictionaries) |
203 | if (!status) { | |
204 | identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
205 | CFIndex i, count = CFArrayGetCount(tmpItems); | |
206 | for (i=0; i<count; i++) { | |
207 | CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i); | |
208 | CFTypeID itemID = CFGetTypeID(anItem); | |
209 | if (itemID == SecIdentityGetTypeID()) { | |
210 | CFMutableDictionaryRef itemDict; | |
427c49bc | 211 | itemDict = CFDictionaryCreateMutable(kCFAllocatorDefault, |
b1ab9ed8 A |
212 | 0, |
213 | &kCFTypeDictionaryKeyCallBacks, | |
214 | &kCFTypeDictionaryValueCallBacks); | |
215 | ||
216 | SecCertificateRef itemCert = NULL; | |
217 | status = SecIdentityCopyCertificate((SecIdentityRef)anItem, &itemCert); | |
427c49bc | 218 | |
b1ab9ed8 A |
219 | // label |
220 | if (!status) { | |
221 | CFStringRef label = SecCertificateCopySubjectSummary(itemCert); | |
222 | if (label) { | |
223 | CFDictionaryAddValue(itemDict, kSecImportItemLabel, label); | |
224 | CFRelease(label); | |
225 | } | |
226 | } | |
227 | ||
228 | // key ID | |
229 | if (!status) { | |
230 | SecKeyRef itemKey = NULL; | |
231 | status = SecCertificateCopyPublicKey(itemCert, &itemKey); | |
232 | if (!status) { | |
233 | const CSSM_KEY *cssmKey; | |
234 | status = SecKeyGetCSSMKey(itemKey, &cssmKey); | |
235 | if (!status) { | |
236 | unsigned char hash[CC_SHA1_DIGEST_LENGTH]; | |
427c49bc | 237 | CC_SHA1(cssmKey->KeyData.Data, (CC_LONG)cssmKey->KeyData.Length, &hash[0]); |
b1ab9ed8 A |
238 | CFDataRef digest = CFDataCreate(NULL, (const UInt8 *)hash, CC_SHA1_DIGEST_LENGTH); |
239 | if (digest) { | |
240 | CFDictionaryAddValue(itemDict, kSecImportItemKeyID, digest); | |
241 | CFRelease(digest); | |
242 | } | |
243 | } | |
244 | CFRelease(itemKey); | |
245 | } | |
246 | } | |
247 | ||
248 | // trust | |
249 | SecTrustRef trust = NULL; | |
250 | SecPolicyRef policy = SecPolicyCreateBasicX509(); | |
251 | CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
252 | CFArrayAppendValue(certArray, itemCert); | |
253 | if (certs) { | |
254 | CFArrayAppendArray(certArray, certs, CFRangeMake(0, CFArrayGetCount(certs))); | |
255 | } | |
256 | status = SecTrustCreateWithCertificates(certArray, policy, &trust); | |
257 | if (policy) { | |
258 | CFRelease(policy); | |
259 | } | |
260 | if (trust) { | |
261 | CFDictionaryAddValue(itemDict, kSecImportItemTrust, trust); | |
262 | CFRelease(trust); | |
263 | } | |
264 | ||
265 | // certificate chain | |
266 | if (certArray) { | |
267 | CFDictionaryAddValue(itemDict, kSecImportItemCertChain, certArray); | |
268 | CFRelease(certArray); | |
269 | } | |
427c49bc | 270 | |
b1ab9ed8 A |
271 | // identity |
272 | CFDictionaryAddValue(itemDict, kSecImportItemIdentity, anItem); | |
427c49bc | 273 | |
b1ab9ed8 | 274 | if (itemCert) |
427c49bc | 275 | CFRelease(itemCert); |
b1ab9ed8 A |
276 | CFArrayAppendValue(identities, itemDict); |
277 | CFRelease(itemDict); | |
278 | } | |
279 | } | |
280 | } | |
427c49bc | 281 | |
b1ab9ed8 A |
282 | if (items) |
283 | *items = identities; | |
284 | else if (identities) | |
285 | CFRelease(identities); | |
286 | ||
287 | if (certs) | |
288 | CFRelease(certs); | |
289 | if (tmpItems) | |
290 | CFRelease(tmpItems); | |
291 | if (importKeychain) | |
292 | CFRelease(importKeychain); | |
293 | if (importAccess) | |
294 | CFRelease(importAccess); | |
295 | if (importPassword) | |
296 | CFRelease(importPassword); | |
427c49bc | 297 | |
b1ab9ed8 | 298 | return status; |
427c49bc | 299 | |
b1ab9ed8 A |
300 | //FIXME: needs SecAsn1Coder implementation |
301 | #if 0 | |
302 | pkcs12_context context = {}; | |
303 | SecAsn1CoderCreate(&context.coder); | |
304 | if (options) | |
305 | context.passphrase = CFDictionaryGetValue(options, kSecImportExportPassphrase); | |
427c49bc | 306 | context.items = CFDictionaryCreateMutable(kCFAllocatorDefault, |
b1ab9ed8 A |
307 | 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
308 | int status = p12decode(&context, pkcs12_data); | |
309 | if (!status) { | |
310 | CFMutableArrayRef certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
311 | CFDictionaryApplyFunction(context.items, collect_certs, certs); | |
312 | ||
313 | CFMutableArrayRef identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
314 | build_trust_chains_context a_build_trust_chains_context = { identities, certs }; | |
315 | CFDictionaryApplyFunction(context.items, build_trust_chains, &a_build_trust_chains_context); | |
316 | CFReleaseSafe(certs); | |
427c49bc | 317 | |
b1ab9ed8 | 318 | /* ignoring certs that weren't picked up as part of the certchain for found keys */ |
427c49bc | 319 | |
b1ab9ed8 A |
320 | *items = identities; |
321 | } | |
322 | ||
323 | CFReleaseSafe(context.items); | |
324 | SecAsn1CoderRelease(context.coder); | |
427c49bc | 325 | |
b1ab9ed8 | 326 | switch (status) { |
427c49bc | 327 | case p12_noErr: return errSecSuccess; |
b1ab9ed8 A |
328 | case p12_passwordErr: return errSecAuthFailed; |
329 | case p12_decodeErr: return errSecDecode; | |
330 | default: return errSecInternal; | |
331 | }; | |
427c49bc | 332 | return errSecSuccess; |
b1ab9ed8 A |
333 | #endif |
334 | } | |
335 |