]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | * | |
23 | * SecExport.cpp - high-level facility for exporting Sec layer objects. | |
24 | */ | |
25 | ||
26 | #include <Security/SecImportExport.h> | |
27 | #include "SecImportExportAgg.h" | |
28 | #include "SecImportExportPem.h" | |
29 | #include "SecExternalRep.h" | |
30 | #include "SecImportExportUtils.h" | |
31 | #include <security_utilities/errors.h> | |
32 | #include <Security/SecIdentity.h> | |
33 | #include <Security/SecIdentityPriv.h> | |
34 | #include <Security/SecItem.h> | |
35 | #include <Security/SecBase.h> | |
36 | #include <security_utilities/simulatecrash_assert.h> | |
37 | using namespace Security; | |
38 | using namespace KeychainCore; | |
39 | ||
40 | /* | |
41 | * Convert Sec item to one or two SecExportReps, append to exportReps array. | |
42 | * The "one or two" clause exists for SecIdentityRefs, which we split into | |
43 | * a cert and a key. | |
44 | * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef, | |
45 | * SecCertRef, or SecIdentityRef. | |
46 | */ | |
47 | static void impExpAddToExportReps( | |
48 | CFTypeRef thing, // Key, Cert, Identity | |
49 | CFMutableArrayRef exportReps, | |
50 | unsigned &numCerts, // IN/OUT - accumulated | |
51 | unsigned &numKeys) // IN/OUT - accumulated | |
52 | { | |
53 | if(CFGetTypeID(thing) == SecIdentityGetTypeID()) { | |
54 | /* special case for SecIdentities, creates two SecExportReps */ | |
55 | OSStatus ortn; | |
56 | SecIdentityRef idRef = (SecIdentityRef)thing; | |
57 | SecCertificateRef certRef; | |
58 | SecKeyRef keyRef; | |
59 | SecExportRep *rep; | |
60 | ||
61 | /* cert */ | |
62 | SecImpExpDbg("impExpAddToExportReps: adding identity cert and key"); | |
63 | ortn = SecIdentityCopyCertificate(idRef, &certRef); | |
64 | if(ortn) { | |
65 | Security::MacOSError::throwMe(ortn); | |
66 | } | |
67 | rep = SecExportRep::vend(certRef); | |
68 | CFArrayAppendValue(exportReps, rep); | |
69 | CFRelease(certRef); // SecExportRep holds a reference | |
70 | numCerts++; | |
71 | ||
72 | /* private key */ | |
73 | ortn = SecIdentityCopyPrivateKey(idRef, &keyRef); | |
74 | if(ortn) { | |
75 | Security::MacOSError::throwMe(ortn); | |
76 | } | |
77 | rep = SecExportRep::vend(keyRef); | |
78 | CFArrayAppendValue(exportReps, rep); | |
79 | CFRelease(keyRef); // SecExportRep holds a reference | |
80 | numKeys++; | |
81 | } | |
82 | else { | |
83 | /* this throws if 'thing' is an unacceptable type */ | |
84 | SecExportRep *rep = SecExportRep::vend(thing); | |
85 | SecImpExpDbg("impExpAddToExportReps: adding single type %d", | |
86 | (int)rep->externType()); | |
87 | CFArrayAppendValue(exportReps, rep); | |
88 | if(rep->externType() == kSecItemTypeCertificate) { | |
89 | numCerts++; | |
90 | } | |
91 | else { | |
92 | numKeys++; | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | #pragma mark --- public export function --- | |
98 | ||
99 | OSStatus SecKeychainItemExport( | |
100 | CFTypeRef keychainItemOrArray, | |
101 | SecExternalFormat outputFormat, // a SecExternalFormat | |
102 | SecItemImportExportFlags flags, // kSecItemPemArmour, etc. | |
103 | const SecKeyImportExportParameters *keyParams, // optional | |
104 | CFDataRef *exportedData) // external representation | |
105 | // returned here | |
106 | { | |
107 | BEGIN_IMP_EXP_SECAPI | |
108 | ||
109 | /* some basic input validation */ | |
110 | if(keychainItemOrArray == NULL) { | |
111 | return errSecParam; | |
112 | } | |
113 | if(keyParams != NULL) { | |
114 | /* can't specify explicit passphrase and ask for secure one */ | |
115 | if( (keyParams->passphrase != NULL) && | |
116 | ((keyParams->flags & kSecKeySecurePassphrase) != 0)) { | |
117 | return errSecParam; | |
118 | } | |
119 | } | |
120 | ||
121 | unsigned numKeys = 0; | |
122 | unsigned numCerts = 0; | |
123 | unsigned numTotalExports = 0; | |
124 | OSStatus ortn = errSecSuccess; | |
125 | SecExportRep *rep = NULL; // common temp variable | |
126 | CFMutableDataRef outputData = NULL; | |
127 | const char *pemHeader = "UNKNOWN"; | |
128 | ||
129 | /* convert keychainItemOrArray to CFArray of SecExportReps */ | |
130 | CFMutableArrayRef exportReps = CFArrayCreateMutable(NULL, 0, NULL); | |
131 | /* subsequent errors to errOut: */ | |
132 | ||
133 | try { | |
134 | if(CFGetTypeID(keychainItemOrArray) == CFArrayGetTypeID()) { | |
135 | CFArrayRef arr = (CFArrayRef)keychainItemOrArray; | |
136 | CFIndex arraySize = CFArrayGetCount(arr); | |
137 | for(CFIndex dex=0; dex<arraySize; dex++) { | |
138 | impExpAddToExportReps(CFArrayGetValueAtIndex(arr, dex), | |
139 | exportReps, numCerts, numKeys); | |
140 | } | |
141 | } | |
142 | else { | |
143 | impExpAddToExportReps(keychainItemOrArray, exportReps, numCerts, numKeys); | |
144 | } | |
145 | } | |
146 | catch(const Security::MacOSError osErr) { | |
147 | ortn = osErr.error; | |
148 | goto errOut; | |
149 | } | |
150 | catch(...) { | |
151 | ortn = errSecParam; | |
152 | goto errOut; | |
153 | } | |
154 | numTotalExports = (unsigned int)CFArrayGetCount(exportReps); | |
155 | assert((numCerts + numKeys) == numTotalExports); | |
156 | if((numTotalExports > 1) && (outputFormat == kSecFormatUnknown)) { | |
157 | /* default aggregate format is PEM sequence */ | |
158 | outputFormat = kSecFormatPEMSequence; | |
159 | } | |
160 | ||
161 | /* | |
162 | * Break out to SecExternalFormat-specific code, appending all data to outputData | |
163 | */ | |
164 | outputData = CFDataCreateMutable(NULL, 0); | |
165 | switch(outputFormat) { | |
166 | case kSecFormatPKCS7: | |
167 | ortn = impExpPkcs7Export(exportReps, flags, keyParams, outputData); | |
168 | pemHeader = PEM_STRING_PKCS7; | |
169 | break; | |
170 | case kSecFormatPKCS12: | |
171 | ortn = impExpPkcs12Export(exportReps, flags, keyParams, outputData); | |
172 | pemHeader = PEM_STRING_PKCS12; | |
173 | break; | |
174 | case kSecFormatPEMSequence: | |
175 | { | |
176 | /* | |
177 | * A bit of a special case. Create an intermediate DER encoding | |
178 | * of each SecExportRef, in the default format for that item; | |
179 | * PEM encode the result, and append the PEM encoding to | |
180 | * outputData. | |
181 | */ | |
182 | CFIndex numReps = CFArrayGetCount(exportReps); | |
183 | for(CFIndex dex=0; dex<numReps; dex++) { | |
184 | ||
185 | rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex); | |
186 | ||
187 | /* default DER encoding */ | |
188 | CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0); | |
189 | ortn = rep->exportRep(kSecFormatUnknown, flags, keyParams, | |
190 | tmpData, &pemHeader); | |
191 | if(ortn) { | |
192 | SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData); | |
193 | CFRelease(tmpData); | |
194 | goto errOut; | |
195 | } | |
196 | ||
197 | /* PEM to accumulating output */ | |
198 | assert(rep->pemParamLines() == NULL); | |
199 | ortn = impExpPemEncodeExportRep((CFDataRef)tmpData, | |
200 | pemHeader, NULL, /* no pemParamLines, right? */ | |
201 | outputData); | |
202 | CFRelease(tmpData); | |
203 | if(ortn) { | |
204 | goto errOut; | |
205 | } | |
206 | } | |
207 | break; | |
208 | } | |
209 | ||
210 | /* Enumerate remainder explicitly for clarity; all are single-item forms */ | |
211 | case kSecFormatOpenSSL: | |
212 | case kSecFormatSSH: | |
213 | case kSecFormatSSHv2: | |
214 | case kSecFormatBSAFE: | |
215 | case kSecFormatRawKey: | |
216 | case kSecFormatWrappedPKCS8: | |
217 | case kSecFormatWrappedOpenSSL: | |
218 | case kSecFormatWrappedSSH: | |
219 | case kSecFormatWrappedLSH: | |
220 | case kSecFormatX509Cert: | |
221 | case kSecFormatUnknown: // i.e., default, handled by SecExportRep | |
222 | { | |
223 | unsigned foundCount = 0; | |
224 | ||
225 | /* verify that we have exactly one of specified item */ | |
226 | if(outputFormat == kSecFormatX509Cert) { | |
227 | foundCount = numCerts; | |
228 | } | |
229 | else if(outputFormat == kSecFormatUnknown) { | |
230 | /* can't go wrong */ | |
231 | foundCount = numTotalExports; | |
232 | } | |
233 | else { | |
234 | foundCount = numKeys; | |
235 | } | |
236 | if((numTotalExports != 1) || (foundCount != 1)) { | |
237 | SecImpExpDbg("Export single item format with other than one item"); | |
238 | ortn = errSecParam; | |
239 | goto errOut; | |
240 | } | |
241 | assert(CFArrayGetCount(exportReps) == 1); | |
242 | rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0); | |
243 | ortn = rep->exportRep(outputFormat, flags, | |
244 | keyParams, outputData, &pemHeader); | |
245 | break; | |
246 | } | |
247 | default: | |
248 | SecImpExpDbg("SecKeychainItemExport: bad format (%u)", | |
249 | (unsigned)outputFormat); | |
250 | ortn = errSecParam; | |
251 | goto errOut; | |
252 | } | |
253 | ||
254 | /* | |
255 | * Final step: possible PEM encode. Skip for kSecFormatPEMSequence (in which | |
256 | * case outputData is all ready to ship out to the caller); mandatory | |
257 | * if exportRep has a non-NULL pemParamLines (which can only happen if we're | |
258 | * exporting a single item). | |
259 | */ | |
260 | if(ortn == errSecSuccess) { | |
261 | if(outputFormat == kSecFormatPEMSequence) { | |
262 | *exportedData = outputData; | |
263 | outputData = NULL; | |
264 | } | |
265 | else { | |
266 | rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0); | |
267 | if((flags & kSecItemPemArmour) || (rep->pemParamLines() != NULL)) { | |
268 | /* PEM encode a single item */ | |
269 | CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0); | |
270 | ortn = impExpPemEncodeExportRep((CFDataRef)outputData, pemHeader, | |
271 | rep->pemParamLines(), tmpData); | |
272 | CFRelease(outputData); // done with this | |
273 | outputData = NULL; | |
274 | *exportedData = tmpData; // caller gets PEM | |
275 | } | |
276 | else { | |
277 | *exportedData = outputData; | |
278 | outputData = NULL; | |
279 | } | |
280 | } | |
281 | } | |
282 | errOut: | |
283 | if(exportReps != NULL) { | |
284 | /* CFArray of our own classes, no auto release */ | |
285 | CFIndex num = CFArrayGetCount(exportReps); | |
286 | for(CFIndex dex=0; dex<num; dex++) { | |
287 | rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex); | |
288 | delete rep; | |
289 | } | |
290 | CFRelease(exportReps); | |
291 | } | |
292 | if(outputData != NULL) { | |
293 | CFRelease(outputData); | |
294 | outputData = NULL; | |
295 | } | |
296 | if(ortn) { | |
297 | return SecKeychainErrFromOSStatus(ortn); | |
298 | } | |
299 | else { | |
300 | return errSecSuccess; | |
301 | } | |
302 | ||
303 | END_IMP_EXP_SECAPI | |
304 | } | |
305 | ||
306 | ||
307 | OSStatus SecItemExport(CFTypeRef secItemOrArray, SecExternalFormat outputFormat, | |
308 | SecItemImportExportFlags flags, /* kSecItemPemArmor, etc. */ | |
309 | const SecItemImportExportKeyParameters *keyParams, /* optional */ | |
310 | CFDataRef *exportedData) | |
311 | { | |
312 | SecKeyImportExportParameters* oldStructPtr = NULL; | |
313 | SecKeyImportExportParameters oldStruct; | |
314 | memset(&oldStruct, 0, sizeof(oldStruct)); | |
315 | ||
316 | if (NULL != keyParams) | |
317 | { | |
318 | ||
319 | SecKeyRef tempKey = NULL; | |
320 | ||
321 | if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray)) | |
322 | { | |
323 | tempKey = (SecKeyRef)secItemOrArray; | |
324 | } | |
325 | ||
326 | if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey, | |
327 | keyParams, &oldStruct)) | |
328 | { | |
329 | oldStructPtr = &oldStruct; | |
330 | } | |
331 | } | |
332 | ||
333 | return SecKeychainItemExport(secItemOrArray, outputFormat, flags, oldStructPtr, exportedData); | |
334 | } | |
335 | ||
336 | ||
337 | ||
338 | ||
339 | ||
340 | ||
341 |