]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_export.c
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTool / keychain_export.c
1 /*
2 * Copyright (c) 2003-2004,2006,2012,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 * keychain_export.c
24 */
25
26 #include "keychain_export.h"
27 #include "keychain_utilities.h"
28 #include "security.h"
29
30 #include <errno.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <Security/SecImportExport.h>
34 #include <Security/SecKeychainItem.h>
35 #include <Security/SecKeychainSearch.h>
36 #include <Security/SecIdentitySearch.h>
37 #include <Security/SecKey.h>
38 #include <Security/SecCertificate.h>
39 #include <security_cdsa_utils/cuFileIo.h>
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <stdio.h>
42
43 typedef enum {
44 IS_Certs,
45 IS_AllKeys,
46 IS_PubKeys,
47 IS_PrivKeys,
48 IS_Identities,
49 IS_All
50 } ItemSpec;
51
52 /*
53 * Add all itmes of specified class from a keychain to an array.
54 * Item class are things like kSecCertificateItemClass, and
55 * CSSM_DL_DB_RECORD_PRIVATE_KEY. Identities are searched separately.
56 */
57 static OSStatus addKcItems(
58 SecKeychainRef kcRef,
59 SecItemClass itemClass, // kSecCertificateItemClass
60 CFMutableArrayRef outArray,
61 unsigned *numItems) // UPDATED on return
62 {
63 OSStatus ortn;
64 SecKeychainSearchRef srchRef;
65
66 ortn = SecKeychainSearchCreateFromAttributes(kcRef,
67 itemClass,
68 NULL, // no attrs
69 &srchRef);
70 if(ortn) {
71 sec_perror("SecKeychainSearchCreateFromAttributes", ortn);
72 return ortn;
73 }
74 for(;;) {
75 SecKeychainItemRef itemRef;
76 ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
77 if(ortn) {
78 if(ortn == errSecItemNotFound) {
79 /* normal search end */
80 ortn = noErr;
81 }
82 else {
83 sec_perror("SecIdentitySearchCopyNext", ortn);
84 }
85 break;
86 }
87 CFArrayAppendValue(outArray, itemRef);
88 CFRelease(itemRef); // array owns the item
89 (*numItems)++;
90 }
91 CFRelease(srchRef);
92 return ortn;
93 }
94
95 /*
96 * Add all SecIdentityRefs from a keychain into an array.
97 */
98 static OSStatus addIdentities(
99 SecKeychainRef kcRef,
100 CFMutableArrayRef outArray,
101 unsigned *numItems) // UPDATED on return
102 {
103 /* Search for all identities */
104 SecIdentitySearchRef srchRef;
105 OSStatus ortn = SecIdentitySearchCreate(kcRef,
106 0, // keyUsage - any
107 &srchRef);
108 if(ortn) {
109 sec_perror("SecIdentitySearchCreate", ortn);
110 return ortn;
111 }
112
113 do {
114 SecIdentityRef identity;
115 ortn = SecIdentitySearchCopyNext(srchRef, &identity);
116 if(ortn) {
117 if(ortn == errSecItemNotFound) {
118 /* normal search end */
119 ortn = noErr;
120 }
121 else {
122 sec_perror("SecIdentitySearchCopyNext", ortn);
123 }
124 break;
125 }
126 CFArrayAppendValue(outArray, identity);
127
128 /* the array has the retain count we need */
129 CFRelease(identity);
130 (*numItems)++;
131 } while(ortn == noErr);
132 CFRelease(srchRef);
133 return ortn;
134 }
135
136 static int do_keychain_export(
137 SecKeychainRef kcRef,
138 SecExternalFormat externFormat,
139 ItemSpec itemSpec,
140 const char *passphrase,
141 int doPem,
142 const char *fileName)
143 {
144 int result = 0;
145 CFIndex numItems;
146 unsigned numPrivKeys = 0;
147 unsigned numPubKeys = 0;
148 unsigned numCerts = 0;
149 unsigned numIdents = 0;
150 OSStatus ortn;
151 uint32 expFlags = 0; // SecItemImportExportFlags
152 SecKeyImportExportParameters keyParams;
153 CFStringRef passStr = NULL;
154 CFDataRef outData = NULL;
155 unsigned len;
156
157 /* gather items */
158 CFMutableArrayRef exportItems = CFArrayCreateMutable(NULL, 0,
159 &kCFTypeArrayCallBacks);
160 switch(itemSpec) {
161 case IS_Certs:
162 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts);
163 if(ortn) {
164 result = 1;
165 goto loser;
166 }
167 break;
168
169 case IS_PrivKeys:
170 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
171 &numPrivKeys);
172 if(ortn) {
173 result = 1;
174 goto loser;
175 }
176 break;
177
178 case IS_PubKeys:
179 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems,
180 &numPubKeys);
181 if(ortn) {
182 result = 1;
183 goto loser;
184 }
185 break;
186
187 case IS_AllKeys:
188 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
189 &numPrivKeys);
190 if(ortn) {
191 result = 1;
192 goto loser;
193 }
194 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems,
195 &numPubKeys);
196 if(ortn) {
197 result = 1;
198 goto loser;
199 }
200 break;
201
202 case IS_All:
203 /* No public keys here - PKCS12 doesn't support them */
204 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts);
205 if(ortn) {
206 result = 1;
207 goto loser;
208 }
209 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
210 &numPrivKeys);
211 if(ortn) {
212 result = 1;
213 goto loser;
214 }
215 break;
216
217 case IS_Identities:
218 ortn = addIdentities(kcRef, exportItems, &numIdents);
219 if(ortn) {
220 result = 1;
221 goto loser;
222 }
223 if(numIdents) {
224 numPrivKeys += numIdents;
225 numCerts += numIdents;
226 }
227 break;
228 default:
229 sec_error("Internal error parsing item_spec");
230 result = 1;
231 goto loser;
232 }
233
234 numItems = CFArrayGetCount(exportItems);
235 if(externFormat == kSecFormatUnknown) {
236 /* Use default export format per set of items */
237 if(numItems > 1) {
238 externFormat = kSecFormatPEMSequence;
239 }
240 else if(numCerts) {
241 externFormat = kSecFormatX509Cert;
242 }
243 else {
244 externFormat = kSecFormatOpenSSL;
245 }
246 }
247 if(doPem) {
248 expFlags |= kSecItemPemArmour;
249 }
250
251 /*
252 * Key related arguments, ignored if we're not exporting keys.
253 * Always specify some kind of passphrase - default is secure passkey.
254 */
255 memset(&keyParams, 0, sizeof(keyParams));
256 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
257 if(passphrase != NULL) {
258 passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII);
259 keyParams.passphrase = passStr;
260 }
261 else {
262 keyParams.flags = kSecKeySecurePassphrase;
263 }
264
265 /* Go */
266 ortn = SecKeychainItemExport(exportItems, externFormat, expFlags, &keyParams,
267 &outData);
268 if(ortn) {
269 sec_perror("SecKeychainItemExport", ortn);
270 result = 1;
271 goto loser;
272 }
273
274 len = CFDataGetLength(outData);
275 if(fileName) {
276 int rtn = writeFile(fileName, CFDataGetBytePtr(outData), len);
277 if(rtn == 0) {
278 if(!do_quiet) {
279 fprintf(stderr, "...%u bytes written to %s\n", len, fileName);
280 }
281 }
282 else {
283 sec_error("Error writing to %s: %s", fileName, strerror(errno));
284 result = 1;
285 }
286 }
287 else {
288 int irtn = write(STDOUT_FILENO, CFDataGetBytePtr(outData), len);
289 if(irtn != (int)len) {
290 perror("write");
291 }
292 }
293 loser:
294 if(exportItems) {
295 CFRelease(exportItems);
296 }
297 if(passStr) {
298 CFRelease(passStr);
299 }
300 if(outData) {
301 CFRelease(outData);
302 }
303 return result;
304 }
305
306 int
307 keychain_export(int argc, char * const *argv)
308 {
309 int ch, result = 0;
310
311 char *outFile = NULL;
312 char *kcName = NULL;
313 SecKeychainRef kcRef = NULL;
314 SecExternalFormat externFormat = kSecFormatUnknown;
315 ItemSpec itemSpec = IS_All;
316 int wrapped = 0;
317 int doPem = 0;
318 const char *passphrase = NULL;
319
320 while ((ch = getopt(argc, argv, "k:o:t:f:P:wph")) != -1)
321 {
322 switch (ch)
323 {
324 case 'k':
325 kcName = optarg;
326 break;
327 case 'o':
328 outFile = optarg;
329 break;
330 case 't':
331 if(!strcmp("certs", optarg)) {
332 itemSpec = IS_Certs;
333 }
334 else if(!strcmp("allKeys", optarg)) {
335 itemSpec = IS_AllKeys;
336 }
337 else if(!strcmp("pubKeys", optarg)) {
338 itemSpec = IS_PubKeys;
339 }
340 else if(!strcmp("privKeys", optarg)) {
341 itemSpec = IS_PrivKeys;
342 }
343 else if(!strcmp("identities", optarg)) {
344 itemSpec = IS_Identities;
345 }
346 else if(!strcmp("all", optarg)) {
347 itemSpec = IS_All;
348 }
349 else {
350 return 2; /* @@@ Return 2 triggers usage message. */
351 }
352 break;
353 case 'f':
354 if(!strcmp("openssl", optarg)) {
355 externFormat = kSecFormatOpenSSL;
356 }
357 else if(!strcmp("openssh1", optarg)) {
358 externFormat = kSecFormatSSH;
359 }
360 else if(!strcmp("openssh2", optarg)) {
361 externFormat = kSecFormatSSHv2;
362 }
363 else if(!strcmp("bsafe", optarg)) {
364 externFormat = kSecFormatBSAFE;
365 }
366 else if(!strcmp("raw", optarg)) {
367 externFormat = kSecFormatRawKey;
368 }
369 else if(!strcmp("pkcs7", optarg)) {
370 externFormat = kSecFormatPKCS7;
371 }
372 else if(!strcmp("pkcs8", optarg)) {
373 externFormat = kSecFormatWrappedPKCS8;
374 }
375 else if(!strcmp("pkcs12", optarg)) {
376 externFormat = kSecFormatPKCS12;
377 }
378 else if(!strcmp("netscape", optarg)) {
379 externFormat = kSecFormatNetscapeCertSequence;
380 }
381 else if(!strcmp("x509", optarg)) {
382 externFormat = kSecFormatX509Cert;
383 }
384 else if(!strcmp("pemseq", optarg)) {
385 externFormat = kSecFormatPEMSequence;
386 }
387 else {
388 return 2; /* @@@ Return 2 triggers usage message. */
389 }
390 break;
391 case 'w':
392 wrapped = 1;
393 break;
394 case 'p':
395 doPem = 1;
396 break;
397 case 'P':
398 passphrase = optarg;
399 break;
400 case '?':
401 default:
402 return 2; /* @@@ Return 2 triggers usage message. */
403 }
404 }
405
406 if(wrapped) {
407 switch(externFormat) {
408 case kSecFormatOpenSSL:
409 case kSecFormatUnknown: // i.e., use default
410 externFormat = kSecFormatWrappedOpenSSL;
411 break;
412 case kSecFormatSSH:
413 externFormat = kSecFormatWrappedSSH;
414 break;
415 case kSecFormatSSHv2:
416 /* there is no wrappedSSHv2 */
417 externFormat = kSecFormatWrappedOpenSSL;
418 break;
419 case kSecFormatWrappedPKCS8:
420 /* proceed */
421 break;
422 default:
423 sec_error("Don't know how to wrap in specified format/type");
424 return 2; /* @@@ Return 2 triggers usage message. */
425 }
426 }
427
428 if(kcName) {
429 kcRef = keychain_open(kcName);
430 if(kcRef == NULL) {
431 return 1;
432 }
433 }
434 result = do_keychain_export(kcRef, externFormat, itemSpec,
435 passphrase, doPem, outFile);
436
437 if(kcRef) {
438 CFRelease(kcRef);
439 }
440 return result;
441 }