]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_export.m
Security-58286.251.4.tar.gz
[apple/security.git] / SecurityTool / keychain_export.m
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 #import <Foundation/Foundation.h>
27
28 #include "keychain_export.h"
29 #include "keychain_utilities.h"
30 #include "security_tool.h"
31
32 #include <errno.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <Security/SecImportExport.h>
36 #include <Security/SecKeychainItem.h>
37 #include <Security/SecKeychainSearch.h>
38 #include <Security/SecIdentitySearch.h>
39 #include <Security/SecKey.h>
40 #include <Security/SecKeyPriv.h>
41 #include <Security/SecCertificate.h>
42 #include <Security/SecCertificatePriv.h>
43 #include <Security/SecItem.h>
44 #include <Security/SecAccessControl.h>
45 #include <Security/SecAccessControlPriv.h>
46 #include <security_cdsa_utils/cuFileIo.h>
47 #include <CoreFoundation/CoreFoundation.h>
48 #include <stdio.h>
49
50 typedef enum {
51 IS_Certs,
52 IS_AllKeys,
53 IS_PubKeys,
54 IS_PrivKeys,
55 IS_Identities,
56 IS_All
57 } ItemSpec;
58
59 /*
60 * Add all itmes of specified class from a keychain to an array.
61 * Item class are things like kSecCertificateItemClass, and
62 * CSSM_DL_DB_RECORD_PRIVATE_KEY. Identities are searched separately.
63 */
64 static OSStatus addKcItems(
65 SecKeychainRef kcRef,
66 SecItemClass itemClass, // kSecCertificateItemClass
67 CFMutableArrayRef outArray,
68 unsigned *numItems) // UPDATED on return
69 {
70 OSStatus ortn;
71 SecKeychainSearchRef srchRef;
72
73 ortn = SecKeychainSearchCreateFromAttributes(kcRef,
74 itemClass,
75 NULL, // no attrs
76 &srchRef);
77 if(ortn) {
78 sec_perror("SecKeychainSearchCreateFromAttributes", ortn);
79 return ortn;
80 }
81 for(;;) {
82 SecKeychainItemRef itemRef;
83 ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
84 if(ortn) {
85 if(ortn == errSecItemNotFound) {
86 /* normal search end */
87 ortn = noErr;
88 }
89 else {
90 sec_perror("SecIdentitySearchCopyNext", ortn);
91 }
92 break;
93 }
94 CFArrayAppendValue(outArray, itemRef);
95 CFRelease(itemRef); // array owns the item
96 (*numItems)++;
97 }
98 CFRelease(srchRef);
99 return ortn;
100 }
101
102 /*
103 * Add all SecIdentityRefs from a keychain into an array.
104 */
105 static OSStatus addIdentities(
106 SecKeychainRef kcRef,
107 CFMutableArrayRef outArray,
108 unsigned *numItems) // UPDATED on return
109 {
110 /* Search for all identities */
111 SecIdentitySearchRef srchRef;
112 OSStatus ortn = SecIdentitySearchCreate(kcRef,
113 0, // keyUsage - any
114 &srchRef);
115 if(ortn) {
116 sec_perror("SecIdentitySearchCreate", ortn);
117 return ortn;
118 }
119
120 do {
121 SecIdentityRef identity;
122 ortn = SecIdentitySearchCopyNext(srchRef, &identity);
123 if(ortn) {
124 if(ortn == errSecItemNotFound) {
125 /* normal search end */
126 ortn = noErr;
127 }
128 else {
129 sec_perror("SecIdentitySearchCopyNext", ortn);
130 }
131 break;
132 }
133 CFArrayAppendValue(outArray, identity);
134
135 /* the array has the retain count we need */
136 CFRelease(identity);
137 (*numItems)++;
138 } while(ortn == noErr);
139 CFRelease(srchRef);
140 return ortn;
141 }
142
143 static int do_keychain_export(
144 SecKeychainRef kcRef,
145 SecExternalFormat externFormat,
146 ItemSpec itemSpec,
147 const char *passphrase,
148 int doPem,
149 const char *fileName)
150 {
151 int result = 0;
152 CFIndex numItems;
153 unsigned numPrivKeys = 0;
154 unsigned numPubKeys = 0;
155 unsigned numCerts = 0;
156 unsigned numIdents = 0;
157 OSStatus ortn;
158 uint32 expFlags = 0; // SecItemImportExportFlags
159 SecKeyImportExportParameters keyParams;
160 CFStringRef passStr = NULL;
161 CFDataRef outData = NULL;
162 size_t len;
163
164 /* gather items */
165 CFMutableArrayRef exportItems = CFArrayCreateMutable(NULL, 0,
166 &kCFTypeArrayCallBacks);
167 switch(itemSpec) {
168 case IS_Certs:
169 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts);
170 if(ortn) {
171 result = 1;
172 goto loser;
173 }
174 break;
175
176 case IS_PrivKeys:
177 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
178 &numPrivKeys);
179 if(ortn) {
180 result = 1;
181 goto loser;
182 }
183 break;
184
185 case IS_PubKeys:
186 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems,
187 &numPubKeys);
188 if(ortn) {
189 result = 1;
190 goto loser;
191 }
192 break;
193
194 case IS_AllKeys:
195 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
196 &numPrivKeys);
197 if(ortn) {
198 result = 1;
199 goto loser;
200 }
201 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems,
202 &numPubKeys);
203 if(ortn) {
204 result = 1;
205 goto loser;
206 }
207 break;
208
209 case IS_All:
210 /* No public keys here - PKCS12 doesn't support them */
211 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts);
212 if(ortn) {
213 result = 1;
214 goto loser;
215 }
216 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
217 &numPrivKeys);
218 if(ortn) {
219 result = 1;
220 goto loser;
221 }
222 break;
223
224 case IS_Identities:
225 ortn = addIdentities(kcRef, exportItems, &numIdents);
226 if(ortn) {
227 result = 1;
228 goto loser;
229 }
230 if(numIdents) {
231 numPrivKeys += numIdents;
232 numCerts += numIdents;
233 }
234 break;
235 default:
236 sec_error("Internal error parsing item_spec");
237 result = 1;
238 goto loser;
239 }
240
241 numItems = CFArrayGetCount(exportItems);
242 if(externFormat == kSecFormatUnknown) {
243 /* Use default export format per set of items */
244 if(numItems > 1) {
245 externFormat = kSecFormatPEMSequence;
246 }
247 else if(numCerts) {
248 externFormat = kSecFormatX509Cert;
249 }
250 else {
251 externFormat = kSecFormatOpenSSL;
252 }
253 }
254 if(doPem) {
255 expFlags |= kSecItemPemArmour;
256 }
257
258 /*
259 * Key related arguments, ignored if we're not exporting keys.
260 * Always specify some kind of passphrase - default is secure passkey.
261 */
262 memset(&keyParams, 0, sizeof(keyParams));
263 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
264 if(passphrase != NULL) {
265 passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII);
266 keyParams.passphrase = passStr;
267 }
268 else {
269 keyParams.flags = kSecKeySecurePassphrase;
270 }
271
272 /* Go */
273 ortn = SecKeychainItemExport(exportItems, externFormat, expFlags, &keyParams,
274 &outData);
275 if(ortn) {
276 sec_perror("SecKeychainItemExport", ortn);
277 result = 1;
278 goto loser;
279 }
280
281 len = CFDataGetLength(outData);
282 if(fileName) {
283 int rtn = writeFileSizet(fileName, CFDataGetBytePtr(outData), len);
284 if(rtn == 0) {
285 if(!do_quiet) {
286 fprintf(stderr, "...%lu bytes written to %s\n", len, fileName);
287 }
288 }
289 else {
290 sec_error("Error writing to %s: %s", fileName, strerror(errno));
291 result = 1;
292 }
293 }
294 else {
295 size_t irtn = write(STDOUT_FILENO, CFDataGetBytePtr(outData), len);
296 if(irtn != (size_t)len) {
297 perror("write");
298 }
299 }
300 loser:
301 if(exportItems) {
302 CFRelease(exportItems);
303 }
304 if(passStr) {
305 CFRelease(passStr);
306 }
307 if(outData) {
308 CFRelease(outData);
309 }
310 return result;
311 }
312
313 int
314 keychain_export(int argc, char * const *argv)
315 {
316 int ch, result = 0;
317
318 char *outFile = NULL;
319 char *kcName = NULL;
320 SecKeychainRef kcRef = NULL;
321 SecExternalFormat externFormat = kSecFormatUnknown;
322 ItemSpec itemSpec = IS_All;
323 int wrapped = 0;
324 int doPem = 0;
325 const char *passphrase = NULL;
326
327 while ((ch = getopt(argc, argv, "k:o:t:f:P:wph")) != -1)
328 {
329 switch (ch)
330 {
331 case 'k':
332 kcName = optarg;
333 break;
334 case 'o':
335 outFile = optarg;
336 break;
337 case 't':
338 if(!strcmp("certs", optarg)) {
339 itemSpec = IS_Certs;
340 }
341 else if(!strcmp("allKeys", optarg)) {
342 itemSpec = IS_AllKeys;
343 }
344 else if(!strcmp("pubKeys", optarg)) {
345 itemSpec = IS_PubKeys;
346 }
347 else if(!strcmp("privKeys", optarg)) {
348 itemSpec = IS_PrivKeys;
349 }
350 else if(!strcmp("identities", optarg)) {
351 itemSpec = IS_Identities;
352 }
353 else if(!strcmp("all", optarg)) {
354 itemSpec = IS_All;
355 }
356 else {
357 return SHOW_USAGE_MESSAGE;
358 }
359 break;
360 case 'f':
361 if(!strcmp("openssl", optarg)) {
362 externFormat = kSecFormatOpenSSL;
363 }
364 else if(!strcmp("openssh1", optarg)) {
365 externFormat = kSecFormatSSH;
366 }
367 else if(!strcmp("openssh2", optarg)) {
368 externFormat = kSecFormatSSHv2;
369 }
370 else if(!strcmp("bsafe", optarg)) {
371 externFormat = kSecFormatBSAFE;
372 }
373 else if(!strcmp("raw", optarg)) {
374 externFormat = kSecFormatRawKey;
375 }
376 else if(!strcmp("pkcs7", optarg)) {
377 externFormat = kSecFormatPKCS7;
378 }
379 else if(!strcmp("pkcs8", optarg)) {
380 externFormat = kSecFormatWrappedPKCS8;
381 }
382 else if(!strcmp("pkcs12", optarg)) {
383 externFormat = kSecFormatPKCS12;
384 }
385 else if(!strcmp("netscape", optarg)) {
386 externFormat = kSecFormatNetscapeCertSequence;
387 }
388 else if(!strcmp("x509", optarg)) {
389 externFormat = kSecFormatX509Cert;
390 }
391 else if(!strcmp("pemseq", optarg)) {
392 externFormat = kSecFormatPEMSequence;
393 }
394 else {
395 return SHOW_USAGE_MESSAGE;
396 }
397 break;
398 case 'w':
399 wrapped = 1;
400 break;
401 case 'p':
402 doPem = 1;
403 break;
404 case 'P':
405 passphrase = optarg;
406 break;
407 case '?':
408 default:
409 return SHOW_USAGE_MESSAGE;
410 }
411 }
412
413 if(wrapped) {
414 switch(externFormat) {
415 case kSecFormatOpenSSL:
416 case kSecFormatUnknown: // i.e., use default
417 externFormat = kSecFormatWrappedOpenSSL;
418 break;
419 case kSecFormatSSH:
420 externFormat = kSecFormatWrappedSSH;
421 break;
422 case kSecFormatSSHv2:
423 /* there is no wrappedSSHv2 */
424 externFormat = kSecFormatWrappedOpenSSL;
425 break;
426 case kSecFormatWrappedPKCS8:
427 /* proceed */
428 break;
429 default:
430 sec_error("Don't know how to wrap in specified format/type");
431 return SHOW_USAGE_MESSAGE;
432 }
433 }
434
435 if(kcName) {
436 kcRef = keychain_open(kcName);
437 if(kcRef == NULL) {
438 return 1;
439 }
440 }
441 result = do_keychain_export(kcRef, externFormat, itemSpec,
442 passphrase, doPem, outFile);
443
444 if(kcRef) {
445 CFRelease(kcRef);
446 }
447 return result;
448 }
449
450 typedef struct {
451 CFMutableStringRef str;
452 } ctk_dict2str_context;
453
454
455 static void
456 ctk_obj_to_str(CFTypeRef obj, char *buf, int bufLen, Boolean key);
457
458 static void
459 ctk_dict2str(const void *key, const void *value, void *context)
460 {
461 char keyBuf[64] = { 0 };
462 ctk_obj_to_str(key, keyBuf, sizeof(keyBuf), true);
463
464 char valueBuf[1024] = { 0 };
465 ctk_obj_to_str(value, valueBuf, sizeof(valueBuf), false);
466
467 CFStringRef str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("\n\t\t\t%s : %s,"), keyBuf, valueBuf);
468 CFStringAppend(((ctk_dict2str_context *)context)->str, str);
469 CFRelease(str);
470 }
471
472 static void
473 ctk_obj_to_str(CFTypeRef obj, char *buf, int bufLen, Boolean key)
474 {
475 CFStringRef str = NULL;
476
477 if(CFGetTypeID(obj) == CFStringGetTypeID()) {
478 // CFStringRef - print the string as is (for keys) or quoted (values)
479 str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, key ? CFSTR("%@") : CFSTR("\"%@\""), obj);
480 } else if(CFGetTypeID(obj) == CFNumberGetTypeID()) {
481 // CFNumber - print the value using current locale
482 CFNumberRef num = (CFNumberRef)obj;
483
484 CFLocaleRef locale = CFLocaleCopyCurrent();
485 CFNumberFormatterRef fmt = CFNumberFormatterCreate(kCFAllocatorDefault, locale, kCFNumberFormatterDecimalStyle);
486 CFRelease(locale);
487
488 str = CFNumberFormatterCreateStringWithNumber(kCFAllocatorDefault, fmt, num);
489 CFRelease(fmt);
490 } else if(CFGetTypeID(obj) == CFDataGetTypeID()) {
491 // CFData - print the data as <hex bytes>
492 CFDataRef data = (CFDataRef)obj;
493
494 CFMutableStringRef hexStr = CFStringCreateMutable(kCFAllocatorDefault, CFDataGetLength(data) * 3);
495
496 for(int i = 0; i < CFDataGetLength(data); i++) {
497 CFStringRef hexByte = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x "), *(CFDataGetBytePtr(data) + i));
498 CFStringAppend(hexStr, hexByte);
499 CFRelease(hexByte);
500 }
501
502 // Get rid of the last excessive space.
503 if(CFDataGetLength(data)) {
504 CFStringDelete(hexStr, CFRangeMake(CFStringGetLength(hexStr) - 1, 1));
505 }
506
507 str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%@>"), hexStr);
508 CFRelease(hexStr);
509 } else if(CFGetTypeID(obj) == CFBooleanGetTypeID()) {
510 // CFBoolean - print true/false
511 CFBooleanRef cfbool = (CFBooleanRef)obj;
512
513 str = CFStringCreateWithCString(kCFAllocatorDefault, CFBooleanGetValue(cfbool) ? "true" : "false", kCFStringEncodingUTF8);
514 } else if(CFGetTypeID(obj) == SecAccessControlGetTypeID()) {
515 // SecAccessControlRef - print the constraints dictionary
516 SecAccessControlRef ac = (SecAccessControlRef)obj;
517
518 CFDictionaryRef constraints = SecAccessControlGetConstraints(ac);
519 CFMutableStringRef constraintsStr = CFStringCreateMutable(kCFAllocatorDefault, 1024);
520 if(constraints && CFDictionaryGetCount(constraints)) {
521 ctk_dict2str_context context;
522 context.str = constraintsStr;
523 CFDictionaryApplyFunction(constraints, ctk_dict2str, &context);
524 CFStringReplace(constraintsStr, CFRangeMake(CFStringGetLength(constraintsStr) - 1, 1), CFSTR("\n\t\t"));
525 }
526
527 CFDictionaryRef protection = SecAccessControlGetProtection(ac);
528 CFMutableStringRef protectionStr = CFStringCreateMutable(kCFAllocatorDefault, 512);
529 if(protection && CFDictionaryGetCount(protection)) {
530 ctk_dict2str_context context;
531 context.str = protectionStr;
532 CFDictionaryApplyFunction(protection, ctk_dict2str, &context);
533 CFStringReplace(protectionStr, CFRangeMake(CFStringGetLength(protectionStr) - 1, 1), CFSTR("\n\t\t"));
534 }
535
536 str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("constraints: {%@}\n\t\tprotection: {%@}"), constraintsStr, protectionStr);
537 CFRelease(constraintsStr);
538 CFRelease(protectionStr);
539 }
540
541 // Fill the provided buffer with the converted string.
542 if(str) {
543 Boolean success = CFStringGetCString(str, buf, bufLen, kCFStringEncodingUTF8);
544 CFRelease(str);
545
546 if(success) {
547 return;
548 }
549 }
550
551 // Use object description as fallback...
552 CFStringRef description = CFCopyDescription(obj);
553 if(!CFStringGetCString(description, buf, bufLen, kCFStringEncodingUTF8)) {
554 // ...or else we don't know.
555 strncpy(buf, "<?>", bufLen);
556 }
557
558 CFRelease(description);
559 }
560
561 typedef struct {
562 int i;
563 NSString *name;
564 } ctk_print_context;
565
566 OSStatus
567 ctk_dump_item(CFTypeRef item, ctk_print_context *ctx);
568
569 static void
570 ctk_print_dict(const void *key, const void *value, void *context)
571 {
572 char keyBuf[64] = { 0 };
573 ctk_obj_to_str(key, keyBuf, sizeof(keyBuf), true);
574
575 char valueBuf[1024] = { 0 };
576 ctk_obj_to_str(value, valueBuf, sizeof(valueBuf), false);
577
578 printf("\t%s : %s\n", keyBuf, valueBuf);
579 }
580
581 static void
582 ctk_dump_item_header(ctk_print_context *ctx)
583 {
584 printf("\n");
585 printf("==== %s #%d\n", ctx->name.UTF8String, ctx->i);
586 }
587
588 static void
589 ctk_dump_item_footer(ctk_print_context *ctx)
590 {
591 printf("====\n");
592 }
593
594 OSStatus
595 ctk_dump_item(CFTypeRef item, ctk_print_context *ctx)
596 {
597 OSStatus stat = errSecSuccess;
598
599 CFTypeID tid = CFGetTypeID(item);
600 if(tid == CFDictionaryGetTypeID()) {
601 // We expect a dictionary containing item attributes.
602 ctk_dump_item_header(ctx);
603 CFDictionaryApplyFunction((CFDictionaryRef)item, ctk_print_dict, ctx);
604 ctk_dump_item_footer(ctx);
605 } else {
606 stat = errSecInternalComponent;
607 printf("Unexpected item type ID: %lu\n", tid);
608 }
609
610 return stat;
611 }
612
613 static OSStatus
614 ctk_dump_items(CFArrayRef items, id secClass, NSString *name)
615 {
616 OSStatus stat = errSecSuccess;
617
618 ctk_print_context ctx = { 1, name };
619
620 for(CFIndex i = 0; i < CFArrayGetCount(items); i++) {
621 CFTypeRef item = CFArrayGetValueAtIndex(items, i);
622 stat = ctk_dump_item(item, &ctx);
623 ctx.i++;
624
625 if(stat) {
626 break;
627 }
628 }
629
630 return stat;
631 }
632
633 static void
634 exportData(NSData *dataToExport, NSString *fileName, NSString *exportPath, NSString *elementName) {
635 NSMutableString *pem = [NSMutableString new];
636 [pem appendString:[NSString stringWithFormat:@"-----BEGIN %@-----\n", elementName]];
637 NSString *base64Cert = [dataToExport base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength | NSDataBase64EncodingEndLineWithLineFeed];
638 [pem appendString:base64Cert];
639 [pem appendString:[NSString stringWithFormat:@"\n-----END %@-----\n", elementName]];
640
641 NSString *fullName = [NSString stringWithFormat:@"%@/%@.pem", exportPath, fileName];
642 NSError *error;
643 [pem writeToFile:fullName atomically:YES encoding:NSUTF8StringEncoding error:&error];
644 if (error) {
645 fprintf(stderr, "%s\n", [NSString stringWithFormat:@"%@", error].UTF8String);
646 }
647 }
648
649 static OSStatus
650 ctk_dump(id secClass, NSString *name, NSString *tid, NSString *exportPath)
651 {
652 NSArray *result;
653 BOOL returnRef = NO;
654
655 if ([secClass isEqual:(id)kSecClassIdentity] || [secClass isEqual:(id)kSecClassCertificate])
656 returnRef = YES;
657
658 NSDictionary *query = @{
659 (id)kSecClass : secClass,
660 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
661 (id)kSecAttrAccessGroup : (id)kSecAttrAccessGroupToken,
662 (id)kSecReturnAttributes : @YES,
663 (id)kSecReturnRef : @(returnRef)
664 };
665
666 if(tid) {
667 NSMutableDictionary *updatedQuery = [NSMutableDictionary dictionaryWithDictionary:query];
668 updatedQuery[(id)kSecAttrTokenID] = tid;
669 query = updatedQuery;
670 }
671
672 OSStatus stat = SecItemCopyMatching((__bridge CFTypeRef)query, (void *)&result);
673 if(stat) {
674 if (stat == errSecItemNotFound) {
675 fprintf(stderr, "No items found.\n");
676 } else {
677 sec_error("SecItemCopyMatching: %x (%d) - %s", stat, stat, sec_errstr(stat));
678 }
679 return stat;
680 }
681
682 // We expect an array of dictionaries containing item attributes as result.
683 if([result isKindOfClass:[NSArray class]]) {
684 if (returnRef) {
685 NSMutableArray *updatedResult = [NSMutableArray array];
686 for (NSDictionary *dict in result) {
687 NSMutableDictionary *updatedItem = [NSMutableDictionary dictionaryWithDictionary:dict];
688 id itemRef = updatedItem[(id)kSecValueRef];
689 if ([secClass isEqual:(id)kSecClassIdentity]) {
690 id certificateRef;
691 if (SecIdentityCopyCertificate((__bridge SecIdentityRef)itemRef, (void *)&certificateRef) != errSecSuccess)
692 continue;
693 itemRef = certificateRef;
694 }
695
696 NSData *certDigest = (__bridge NSData*)SecCertificateGetSHA1Digest((__bridge SecCertificateRef)itemRef);
697 updatedItem[@"sha1"] = certDigest;
698 [updatedItem removeObjectForKey:(id)kSecValueRef];
699 [updatedResult addObject:updatedItem];
700
701 if (exportPath) {
702 NSData *certData = (__bridge_transfer NSData *)SecCertificateCopyData((__bridge SecCertificateRef)itemRef);
703 exportData(certData, updatedItem[(id)kSecAttrLabel], exportPath, @"CERTIFICATE");
704 id publicKey = (__bridge_transfer id)SecCertificateCopyKey((__bridge SecCertificateRef)itemRef);
705 NSData *pubKeyInfo = (__bridge_transfer NSData *)SecKeyCopySubjectPublicKeyInfo((__bridge SecKeyRef)publicKey);
706 exportData(pubKeyInfo, [NSString stringWithFormat:@"Public Key - %@", updatedItem[(id)kSecAttrLabel]], exportPath, @"PUBLIC KEY");
707 }
708 }
709 result = updatedResult;
710 }
711
712 if (!exportPath) {
713 stat = ctk_dump_items((__bridge CFArrayRef)result, secClass, name);
714 }
715 } else {
716 stat = errSecInternalComponent;
717 }
718 return stat;
719 }
720
721 int
722 ctk_export(int argc, char * const *argv)
723 {
724 __block OSStatus stat = errSecSuccess;
725
726 ItemSpec itemSpec = IS_All;
727 NSString *tid;
728 NSString *exportPath;
729 int ch;
730 BOOL optT = NO;
731 BOOL optE = NO;
732
733 while ((ch = getopt(argc, argv, "i:t:e:h")) != -1) {
734 switch (ch) {
735 case 't':
736 if(!strcmp("certs", optarg)) {
737 itemSpec = IS_Certs;
738 }
739 else if(!strcmp("privKeys", optarg)) {
740 itemSpec = IS_PrivKeys;
741 }
742 else if(!strcmp("identities", optarg)) {
743 itemSpec = IS_Identities;
744 }
745 else if(!strcmp("all", optarg)) {
746 itemSpec = IS_All;
747 }
748 else {
749 return SHOW_USAGE_MESSAGE;
750 }
751 optT = YES;
752 break;
753 case 'i':
754 tid = [NSString stringWithUTF8String:optarg];
755 break;
756 case 'e':
757 exportPath = [NSString stringWithUTF8String:optarg];
758 itemSpec = IS_Certs;
759 optE = YES;
760 break;
761
762 case '?':
763 default:
764 return SHOW_USAGE_MESSAGE;
765 }
766 }
767
768 if (optT && optE) {
769 return SHOW_USAGE_MESSAGE;
770 }
771
772 NSDictionary<id, NSArray *> *classesAndNames = @{ (id)kSecClassCertificate : @[ @"certificate", @(IS_Certs) ],
773 (id)kSecClassKey : @[ @"private key", @(IS_PrivKeys) ],
774 (id)kSecClassIdentity : @[ @"identity", @(IS_Identities) ] };
775
776 [classesAndNames enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, NSArray * _Nonnull obj, BOOL * _Nonnull stop) {
777 if (itemSpec == IS_All || itemSpec == ((NSNumber *)obj[1]).unsignedIntegerValue) {
778 stat = ctk_dump(key, obj[0], tid, exportPath);
779 if(stat) {
780 *stop = YES;
781 }
782 }
783 }];
784
785 return stat;
786 }