1/* 2 * kcExport.cpp - export keychain items using SecKeychainItemExport 3 */ 4 5#include <Security/Security.h> 6#include <Security/SecImportExport.h> 7#include <security_cdsa_utils/cuFileIo.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <unistd.h> 12#include <utilLib/common.h> 13#include <clAppUtils/identPicker.h> 14 15static void usage(char **argv) 16{ 17 printf("Usage: %s infile [option ...]\n", argv[0]); 18 printf("Options:\n"); 19 printf(" -k keychain Keychain to import into\n"); 20 printf(" -z passphrase For PKCS12 and wrapped keys only\n"); 21 printf(" -w Private keys are wrapped\n"); 22 printf(" -d Display Imported Items\n"); 23 printf(" -i Interactive: displays items before possible import\n"); 24 printf(" -v Verbose display\n"); 25 printf(" -l Loop & Pause for MallocDebug\n"); 26 printf(" -q Quiet\n"); 27 printf("Import type/format options:\n"); 28 printf(" -t <type> type = pub|priv|session|cert|agg\n"); 29 printf(" -f <format> format = openssl|openssh1|openssh2|bsafe|\n" 30 " raw|pkcs7|pkcs8|pkcs12|netscape|pemseq\n"); 31 printf("Private key options:\n"); 32 printf(" -a appPath Add appPath to list of trusted apps in private key's ACL\n"); 33 printf(" -e Allow private keys to be extractable in the clear\n"); 34 printf(" -n Private keys have *NO* ACL\n"); 35 printf(" -s Private keys can sign (only)\n"); 36 printf("Secure passphrase options:\n"); 37 printf(" -Z Get secure passphrase\n"); 38 printf(" -L title\n"); 39 printf(" -p prompt\n"); 40 printf("Verification options:\n"); 41 printf(" -C numCerts Verify number of certificates\n"); 42 printf(" -K numKeys Verify number of keys\n"); 43 printf(" -I numIdents Verify number of identities\n"); 44 printf(" -F rtnFormat Returned format = " 45 "openssl|openssh1|openssh2|bsafe|raw|pkcs7|pkcs12|netscape|pemseq\n"); 46 printf(" -T <type> Returned type = pub|priv|session|cert|agg\n"); 47 printf(" -m Set ImportOnlyOneKey bit\n"); 48 printf(" -M Set ImportOnlyOneKey bit and expect errSecMultiplePrivKeys\n"); 49 exit(1); 50} 51 52const char *secExtFormatStr( 53 SecExternalFormat format) 54{ 55 switch(format) { 56 case kSecFormatUnknown: return "kSecFormatUnknown"; 57 case kSecFormatOpenSSL: return "kSecFormatOpenSSL"; 58 case kSecFormatSSH: return "kSecFormatSSH"; 59 case kSecFormatBSAFE: return "kSecFormatBSAFE"; 60 case kSecFormatRawKey: return "kSecFormatRawKey"; 61 case kSecFormatWrappedPKCS8: return "kSecFormatWrappedPKCS8"; 62 case kSecFormatWrappedOpenSSL: return "kSecFormatWrappedOpenSSL"; 63 case kSecFormatWrappedSSH: return "kSecFormatWrappedSSH"; 64 case kSecFormatWrappedLSH: return "kSecFormatWrappedLSH"; 65 case kSecFormatX509Cert: return "kSecFormatX509Cert"; 66 case kSecFormatPEMSequence: return "kSecFormatPEMSequence"; 67 case kSecFormatPKCS7: return "kSecFormatPKCS7"; 68 case kSecFormatPKCS12: return "kSecFormatPKCS12"; 69 case kSecFormatNetscapeCertSequence: return "kSecFormatNetscapeCertSequence"; 70 case kSecFormatSSHv2: return "kSecFormatSSHv2"; 71 default: return "UNKNOWN FORMAT ENUM"; 72 } 73} 74 75const char *secExtItemTypeStr( 76 SecExternalItemType itemType) 77{ 78 switch(itemType) { 79 case kSecItemTypeUnknown: return "kSecItemTypeUnknown"; 80 case kSecItemTypePrivateKey: return "kSecItemTypePrivateKey"; 81 case kSecItemTypePublicKey: return "kSecItemTypePublicKey"; 82 case kSecItemTypeSessionKey: return "kSecItemTypeSessionKey"; 83 case kSecItemTypeCertificate: return "kSecItemTypeCertificate"; 84 case kSecItemTypeAggregate: return "kSecItemTypeAggregate"; 85 default: return "UNKNOWN ITEM TYPE ENUM"; 86 } 87} 88 89static OSStatus processItem( 90 SecKeychainRef keychain, 91 SecKeychainItemRef item, 92 int *numCerts, // IN/OUT 93 int *numKeys, 94 int *numIds, 95 bool interactive, // unimplemented 96 bool verbose) 97{ 98 char *kcName = NULL; 99 CFTypeID itemType = CFGetTypeID(item); 100 if(itemType == SecIdentityGetTypeID()) { 101 (*numIds)++; 102 if(verbose) { 103 /* identities don't have keychains, only their components do */ 104 SecCertificateRef certRef = NULL; 105 OSStatus ortn; 106 ortn = SecIdentityCopyCertificate((SecIdentityRef)item, &certRef); 107 if(ortn) { 108 cssmPerror("SecIdentityCopyCertificate", ortn); 109 return ortn; 110 } 111 kcName = kcItemKcFileName((SecKeychainItemRef)certRef); 112 printf(" identity : cert keychain name %s\n", kcName ? kcName : "<none>"); 113 CFRelease(certRef); 114 115 SecKeyRef keyRef = NULL; 116 ortn = SecIdentityCopyPrivateKey((SecIdentityRef)item, &keyRef); 117 if(ortn) { 118 cssmPerror("SecIdentityCopyPrivateKey", ortn); 119 return ortn; 120 } 121 free(kcName); 122 kcName = kcItemKcFileName((SecKeychainItemRef)keyRef); 123 printf(" identity : key keychain name %s\n", kcName ? kcName : "<none>"); 124 CFRelease(keyRef); 125 } 126 } 127 else if(itemType == SecCertificateGetTypeID()) { 128 (*numCerts)++; 129 if(verbose) { 130 kcName = kcItemKcFileName(item); 131 printf(" cert : keychain name %s\n", kcName ? kcName : "<none>"); 132 } 133 } 134 else if(itemType == SecKeyGetTypeID()) { 135 (*numKeys)++; 136 if(verbose) { 137 kcName = kcItemKcFileName(item); 138 printf(" key : keychain name %s\n", kcName ? kcName : "<none>"); 139 } 140 } 141 /* FIX display attr info, at least names, eventually */ 142 else { 143 printf("***Unknown type returned from SecKeychainItemImport()\n"); 144 return errSecUnknownFormat; 145 } 146 if(kcName) { 147 free(kcName); 148 } 149 return noErr; 150} 151 152static OSStatus processItems( 153 SecKeychainRef keychain, 154 CFArrayRef outArray, 155 int expectNumCerts, // -1 means don't check 156 int expectNumKeys, 157 int expectNumIds, 158 bool interactive, 159 bool displayItems, 160 bool verbose) 161{ 162 int numCerts = 0; 163 int numKeys = 0; 164 int numIds = 0; 165 OSStatus ortn; 166 167 CFIndex numItems = CFArrayGetCount(outArray); 168 for(CFIndex dex=0; dex<numItems; dex++) { 169 ortn = processItem(keychain, 170 (SecKeychainItemRef)CFArrayGetValueAtIndex(outArray, dex), 171 &numCerts, &numKeys, &numIds, interactive, verbose); 172 if(ortn) { 173 break; 174 } 175 } 176 if(ortn) { 177 return ortn; 178 } 179 180 if(displayItems) { 181 printf("Certs found : %d\n", numCerts); 182 printf("Keys found : %d\n", numKeys); 183 printf("Idents found : %d\n", numIds); 184 } 185 if(expectNumCerts >= 0) { 186 if(expectNumCerts != numCerts) { 187 printf("***Expected %d certs, got %d\n", 188 expectNumCerts, numCerts); 189 ortn = -1; 190 } 191 } 192 if(expectNumKeys >= 0) { 193 if(expectNumKeys != numKeys) { 194 printf("***Expected %d keys, got %d\n", 195 expectNumKeys, numKeys); 196 ortn = -1; 197 } 198 } 199 if(expectNumIds >= 0) { 200 if(expectNumIds != numIds) { 201 printf("***Expected %d certs, got %d\n", 202 expectNumIds, numIds); 203 ortn = -1; 204 } 205 } 206 return ortn; 207} 208 209/* 210 * Parse cmd-line format specifier into an SecExternalFormat. 211 * Returns true if it works. 212 */ 213static bool formatFromString( 214 const char *formStr, 215 SecExternalFormat *externFormat) 216{ 217 if(!strcmp("openssl", formStr)) { 218 *externFormat = kSecFormatOpenSSL; 219 } 220 else if(!strcmp("openssh1", formStr)) { 221 *externFormat = kSecFormatSSH; 222 } 223 else if(!strcmp("openssh2", formStr)) { 224 *externFormat = kSecFormatSSHv2; 225 } 226 else if(!strcmp("bsafe", formStr)) { 227 *externFormat = kSecFormatBSAFE; 228 } 229 else if(!strcmp("raw", formStr)) { 230 *externFormat = kSecFormatRawKey; 231 } 232 else if(!strcmp("pkcs7", formStr)) { 233 *externFormat = kSecFormatPKCS7; 234 } 235 else if(!strcmp("pkcs8", formStr)) { 236 *externFormat = kSecFormatWrappedPKCS8; 237 } 238 else if(!strcmp("pkcs12", formStr)) { 239 *externFormat = kSecFormatPKCS12; 240 } 241 else if(!strcmp("netscape", formStr)) { 242 *externFormat = kSecFormatNetscapeCertSequence; 243 } 244 else if(!strcmp("pemseq", formStr)) { 245 *externFormat = kSecFormatPEMSequence; 246 } 247 else { 248 return false; 249 } 250 return true; 251} 252 253 /* 254 * Parse cmd-line type specifier into an SecExternalItemType. 255 * Returns true if it works. 256 */ 257static bool itemTypeFromString( 258 const char *typeStr, 259 SecExternalItemType *itemType) 260{ 261 if(!strcmp("pub", typeStr)) { 262 *itemType = kSecItemTypePublicKey; 263 } 264 else if(!strcmp("priv", typeStr)) { 265 *itemType = kSecItemTypePrivateKey; 266 } 267 else if(!strcmp("session", typeStr)) { 268 *itemType = kSecItemTypeSessionKey; 269 } 270 else if(!strcmp("cert", typeStr)) { 271 *itemType = kSecItemTypeCertificate; 272 } 273 else if(!strcmp("agg", typeStr)) { 274 *itemType = kSecItemTypeAggregate; 275 } 276 else { 277 return false; 278 } 279 return true; 280} 281 282int main(int argc, char **argv) 283{ 284 if(argc < 2) { 285 usage(argv); 286 } 287 288 const char *inFileName = argv[1]; 289 290 extern int optind; 291 extern char *optarg; 292 int arg; 293 optind = 2; 294 int ourRtn = 0; 295 SecAccessRef accessRef = NULL; 296 OSStatus ortn; 297 298 /* optional args */ 299 const char *keychainName = NULL; 300 CFStringRef passphrase = NULL; 301 bool interactive = false; 302 bool securePassphrase = false; 303 SecExternalFormat externFormat = kSecFormatUnknown; 304 SecExternalItemType itemType = kSecItemTypeUnknown; 305 bool doWrap = false; 306 bool allowClearExtract = false; 307 int expectNumCerts = -1; // >=0 means verify per user spec 308 int expectNumKeys = -1; 309 int expectNumIds = -1; 310 bool processOutput = false; 311 bool displayItems = false; 312 SecExternalFormat expectFormat = kSecFormatUnknown; // otherwise verify 313 SecExternalItemType expectItemType = kSecItemTypeUnknown; // ditto 314 bool quiet = false; 315 bool noACL = false; 316 char *alertTitle = NULL; 317 char *alertPrompt = NULL; 318 bool setAllowOnlyOne = false; 319 bool expectMultiKeysError = false; 320 CFMutableArrayRef trustedAppList = NULL; 321 bool loopPause = false; 322 bool signOnly = false; 323 bool verbose = false; 324 325 while ((arg = getopt(argc, argv, "k:iZz:wet:f:C:K:I:F:T:qnL:p:mMa:dlsv")) != -1) { 326 switch (arg) { 327 case 'k': 328 keychainName = optarg; 329 break; 330 case 'i': 331 interactive = true; 332 processOutput = true; 333 break; 334 case 'Z': 335 securePassphrase = true; 336 break; 337 case 'z': 338 passphrase = CFStringCreateWithCString(NULL, optarg, 339 kCFStringEncodingASCII); 340 break; 341 case 'w': 342 doWrap = true; 343 break; 344 case 'e': 345 allowClearExtract = true; 346 break; 347 case 't': 348 if(!itemTypeFromString(optarg, &itemType)) { 349 usage(argv); 350 } 351 break; 352 case 'T': 353 if(!itemTypeFromString(optarg, &expectItemType)) { 354 usage(argv); 355 } 356 break; 357 case 'f': 358 if(!formatFromString(optarg, &externFormat)) { 359 usage(argv); 360 } 361 break; 362 case 'F': 363 if(!formatFromString(optarg, &expectFormat)) { 364 usage(argv); 365 } 366 break; 367 case 'C': 368 expectNumCerts = atoi(optarg); 369 processOutput = true; 370 break; 371 case 'K': 372 expectNumKeys = atoi(optarg); 373 processOutput = true; 374 break; 375 case 'I': 376 expectNumIds = atoi(optarg); 377 processOutput = true; 378 break; 379 case 'd': 380 displayItems = true; 381 processOutput = true; 382 break; 383 case 'q': 384 quiet = true; 385 break; 386 case 'n': 387 noACL = true; 388 break; 389 case 'L': 390 alertTitle = optarg; 391 break; 392 case 'p': 393 alertPrompt = optarg; 394 break; 395 case 'm': 396 setAllowOnlyOne = true; 397 break; 398 case 'M': 399 setAllowOnlyOne = true; 400 expectMultiKeysError = true; 401 break; 402 case 'a': 403 { 404 if(trustedAppList == NULL) { 405 trustedAppList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 406 } 407 SecTrustedApplicationRef appRef; 408 ortn = SecTrustedApplicationCreateFromPath(optarg, &appRef); 409 if(ortn) { 410 cssmPerror("SecTrustedApplicationCreateFromPath", ortn); 411 exit(1); 412 } 413 CFArrayAppendValue(trustedAppList, appRef); 414 break; 415 } 416 case 's': 417 signOnly = true; 418 break; 419 case 'l': 420 loopPause = true; 421 break; 422 case 'v': 423 verbose = true; 424 break; 425 default: 426 case '?': 427 usage(argv); 428 } 429 } 430 if(optind != argc) { 431 /* getopt does not return '?' */ 432 usage(argv); 433 } 434 if(doWrap) { 435 switch(externFormat) { 436 case kSecFormatOpenSSL: 437 case kSecFormatUnknown: // i.e., use default 438 externFormat = kSecFormatWrappedOpenSSL; 439 break; 440 case kSecFormatSSH: 441 externFormat = kSecFormatWrappedSSH; 442 break; 443 case kSecFormatSSHv2: 444 /* there is no wrappedSSHv2 */ 445 externFormat = kSecFormatWrappedOpenSSL; 446 break; 447 case kSecFormatWrappedPKCS8: 448 /* proceed */ 449 break; 450 default: 451 printf("Don't know how to wrap in specified format/type.\n"); 452 exit(1); 453 } 454 } 455 456 CFArrayRef outArray = NULL; 457 CFArrayRef *outArrayP = NULL; 458 if(processOutput) { 459 outArrayP = &outArray; 460 } 461 462 SecKeychainRef kcRef = NULL; 463 if(keychainName) { 464 OSStatus ortn = SecKeychainOpen(keychainName, &kcRef); 465 if(ortn) { 466 cssmPerror("SecKeychainOpen", ortn); 467 exit(1); 468 } 469 /* why is this failing later */ 470 CSSM_DL_DB_HANDLE dlDbHandle; 471 ortn = SecKeychainGetDLDBHandle(kcRef, &dlDbHandle); 472 if(ortn) { 473 cssmPerror("SecKeychainGetDLDBHandle", ortn); 474 exit(1); 475 } 476 477 } 478 479 unsigned char *inFile = NULL; 480 unsigned inFileLen = 0; 481 if(readFile(inFileName, &inFile, &inFileLen)) { 482 printf("***Error reading input file %s. Aborting.\n", inFileName); 483 exit(1); 484 } 485 CFDataRef inFileRef = CFDataCreate(NULL, inFile, inFileLen); 486 CFStringRef fileNameStr = CFStringCreateWithCString(NULL, inFileName, 487 kCFStringEncodingASCII); 488 489loopTop: 490 SecKeyImportExportParameters keyParams; 491 SecKeyImportExportParameters *keyParamPtr = NULL; 492 493 if(passphrase || securePassphrase || allowClearExtract || noACL || 494 setAllowOnlyOne || trustedAppList || signOnly) { 495 keyParamPtr = &keyParams; 496 memset(&keyParams, 0, sizeof(keyParams)); 497 if(securePassphrase) { 498 /* give this precedence */ 499 keyParams.flags |= kSecKeySecurePassphrase; 500 if(alertTitle) { 501 keyParams.alertTitle = 502 CFStringCreateWithCString(NULL, alertTitle, kCFStringEncodingASCII); 503 } 504 if(alertPrompt) { 505 keyParams.alertPrompt = 506 CFStringCreateWithCString(NULL, alertPrompt, kCFStringEncodingASCII); 507 } 508 } 509 else if(passphrase) { 510 keyParams.passphrase = passphrase; 511 } 512 if(noACL) { 513 keyParams.flags |= kSecKeyNoAccessControl; 514 } 515 if(setAllowOnlyOne) { 516 keyParams.flags |= kSecKeyImportOnlyOne; 517 } 518 keyParams.keyAttributes = ( CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE); 519 if(!allowClearExtract) { 520 keyParams.keyAttributes |= CSSM_KEYATTR_SENSITIVE; 521 } 522 if(kcRef) { 523 keyParams.keyAttributes |= CSSM_KEYATTR_PERMANENT; 524 } 525 if(signOnly) { 526 keyParams.keyUsage = CSSM_KEYUSE_SIGN; 527 } 528 else { 529 keyParams.keyUsage = CSSM_KEYUSE_ANY; 530 } 531 if(trustedAppList) { 532 ortn = SecAccessCreate(CFSTR("Imported Private Key"), trustedAppList, &accessRef); 533 if(ortn) { 534 cssmPerror("SecAccessCreate", ortn); 535 exit(1); 536 } 537 keyParams.accessRef = accessRef; 538 } 539 /* TBD other stuff - usage? Other? */ 540 } 541 542 /* GO */ 543 ortn = SecKeychainItemImport(inFileRef, 544 fileNameStr, 545 &externFormat, 546 &itemType, 547 0, // flags 548 keyParamPtr, 549 kcRef, 550 outArrayP); 551 ourRtn = 0; 552 if(ortn) { 553 if(expectMultiKeysError && (ortn == errSecMultiplePrivKeys)) { 554 if(!quiet) { 555 printf("...errSecMultiplePrivKeys error seen as expected\n"); 556 } 557 } 558 else { 559 cssmPerror("SecKeychainItemImport", ortn); 560 ourRtn = -1; 561 } 562 } 563 else if(expectMultiKeysError) { 564 printf("***errSecMultiplePrivKeys expected but no error seen\n"); 565 ourRtn = -1; 566 } 567 if(ortn == noErr) { 568 if(!quiet) { 569 printf("...import successful. Returned format %s\n", 570 secExtFormatStr(externFormat)); 571 } 572 if(expectFormat != kSecFormatUnknown) { 573 if(expectFormat != externFormat) { 574 printf("***Expected format %s, got %s\n", 575 secExtFormatStr(expectFormat), 576 secExtFormatStr(externFormat)); 577 ourRtn = -1; 578 } 579 } 580 if(expectItemType != kSecItemTypeUnknown) { 581 if(expectItemType != itemType) { 582 printf("***Expected itemType %s, got %s\n", 583 secExtItemTypeStr(expectItemType), 584 secExtItemTypeStr(itemType)); 585 ourRtn = -1; 586 } 587 } 588 if(processOutput) { 589 ourRtn |= processItems(kcRef, outArray, expectNumCerts, 590 expectNumKeys, expectNumIds, interactive, displayItems, verbose); 591 } 592 } 593 if(loopPause) { 594 fflush(stdin); 595 printf("Pausing for MallocDebug; CR to continue: "); 596 getchar(); 597 goto loopTop; 598 } 599 return ourRtn; 600} 601