]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/SecExport.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_keychain / lib / SecExport.cpp
1 /*
2 * Copyright (c) 2004 Apple Computer, 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 "SecImportExport.h"
27 #include "SecImportExportAgg.h"
28 #include "SecImportExportPem.h"
29 #include "SecExternalRep.h"
30 #include "SecImportExportUtils.h"
31 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
32 #include <security_utilities/errors.h>
33 #include <Security/SecIdentity.h>
34 #include <Security/SecIdentityPriv.h>
35 #include <Security/SecItem.h>
36
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 paramErr;
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 paramErr;
118 }
119 }
120
121 unsigned numKeys = 0;
122 unsigned numCerts = 0;
123 unsigned numTotalExports = 0;
124 OSStatus ortn = noErr;
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 = paramErr;
152 goto errOut;
153 }
154 numTotalExports = 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 = paramErr;
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 = paramErr;
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 == noErr) {
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 noErr;
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