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 }