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