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 14static void usage(char **argv) 15{ 16 printf("Usage: %s keychain [option ...]\n", argv[0]); 17 printf("Options:\n"); 18 printf(" -t <type> itemType = certs|allKeys|pubKeys|privKeys|identities|all\n"); 19 printf(" ...default itemType=all\n"); 20 printf(" -f <format> format = openssl|openssh1|openssh2|bsafe|pkcs7|pkcs8|pkcs12|pemseq\n" 21 " ...default itemType is pemseq for aggregate, openssl\n" 22 " for single \n"); 23 printf(" -p PEM encode\n"); 24 printf(" -w Private keys are wrapped\n"); 25 printf(" -o outFileName (default is stdout)\n"); 26 printf(" -z passphrase (for PKCS12 and wrapped keys only)\n"); 27 printf(" -Z Use secure passphrase\n"); 28 printf(" -q Quiet\n"); 29 printf(" -h help\n"); 30 exit(1); 31} 32 33typedef enum { 34 IS_Certs, 35 IS_AllKeys, 36 IS_PubKeys, 37 IS_PrivKeys, 38 IS_Identities, 39 IS_All 40} ItemSpec; 41 42/* 43 * Add all itmes of specified class from a keychain to an array. 44 * Item class are things like kSecCertificateItemClass, and 45 * CSSM_DL_DB_RECORD_PRIVATE_KEY. Identities are searched separately. 46 */ 47static OSStatus addKcItems( 48 SecKeychainRef kcRef, 49 SecItemClass itemClass, // kSecCertificateItemClass 50 CFMutableArrayRef outArray) 51{ 52 OSStatus ortn; 53 SecKeychainSearchRef srchRef; 54 55 ortn = SecKeychainSearchCreateFromAttributes(kcRef, 56 itemClass, 57 NULL, // no attrs 58 &srchRef); 59 if(ortn) { 60 cssmPerror("SecKeychainSearchCreateFromAttributes", ortn); 61 return ortn; 62 } 63 for(;;) { 64 SecKeychainItemRef itemRef; 65 ortn = SecKeychainSearchCopyNext(srchRef, &itemRef); 66 if(ortn) { 67 if(ortn == errSecItemNotFound) { 68 /* normal search end */ 69 ortn = noErr; 70 } 71 else { 72 cssmPerror("SecKeychainSearchCopyNext", ortn); 73 } 74 break; 75 } 76 CFArrayAppendValue(outArray, itemRef); 77 CFRelease(itemRef); // array owns it 78 } 79 CFRelease(srchRef); 80 return ortn; 81} 82 83/* 84 * Add all SecIdentityRefs from a keychain into an array. 85 */ 86static OSStatus addIdentities( 87 SecKeychainRef kcRef, 88 CFMutableArrayRef outArray) 89{ 90 /* Search for all identities */ 91 SecIdentitySearchRef srchRef; 92 OSStatus ortn = SecIdentitySearchCreate(kcRef, 93 0, // keyUsage - any 94 &srchRef); 95 if(ortn) { 96 cssmPerror("SecIdentitySearchCreate", ortn); 97 return ortn; 98 } 99 100 do { 101 SecIdentityRef identity; 102 ortn = SecIdentitySearchCopyNext(srchRef, &identity); 103 if(ortn) { 104 if(ortn == errSecItemNotFound) { 105 /* normal search end */ 106 ortn = noErr; 107 } 108 else { 109 cssmPerror("SecIdentitySearchCopyNext", ortn); 110 } 111 break; 112 } 113 CFArrayAppendValue(outArray, identity); 114 115 /* the array has the retain count we need */ 116 CFRelease(identity); 117 } while(ortn == noErr); 118 CFRelease(srchRef); 119 return ortn; 120} 121 122int main(int argc, char **argv) 123{ 124 if(argc < 4) { 125 usage(argv); 126 } 127 128 const char *kcName = argv[1]; 129 SecKeychainRef kcRef = NULL; 130 OSStatus ortn = SecKeychainOpen(kcName, &kcRef); 131 if(ortn) { 132 cssmPerror("SecKeychainOpen", ortn); 133 exit(1); 134 } 135 136 /* user specified options */ 137 ItemSpec itemSpec = IS_All; // default 138 SecExternalFormat exportForm = kSecFormatUnknown; 139 bool pemEncode = false; 140 const char *outFile = NULL; 141 CFStringRef passphrase = NULL; 142 bool securePassphrase = false; 143 bool quiet = false; 144 bool wrapPrivKeys = false; 145 146 extern int optind; 147 extern char *optarg; 148 int arg; 149 optind = 2; 150 151 while ((arg = getopt(argc, argv, "t:f:po:z:Zhqw")) != -1) { 152 switch (arg) { 153 case 't': 154 if(!strcmp("certs", optarg)) { 155 itemSpec = IS_Certs; 156 } 157 else if(!strcmp("allKeys", optarg)) { 158 itemSpec = IS_AllKeys; 159 } 160 else if(!strcmp("pubKeys", optarg)) { 161 itemSpec = IS_PubKeys; 162 } 163 else if(!strcmp("privKeys", optarg)) { 164 itemSpec = IS_PrivKeys; 165 } 166 else if(!strcmp("identities", optarg)) { 167 itemSpec = IS_Identities; 168 } 169 else if(!strcmp("all", optarg)) { 170 itemSpec = IS_All; 171 } 172 else { 173 usage(argv); 174 } 175 break; 176 case 'f': 177 if(!strcmp("openssl", optarg)) { 178 exportForm = kSecFormatOpenSSL; 179 } 180 else if(!strcmp("openssh1", optarg)) { 181 exportForm = kSecFormatSSH; 182 } 183 else if(!strcmp("openssh2", optarg)) { 184 exportForm = kSecFormatSSHv2; 185 } 186 else if(!strcmp("bsafe", optarg)) { 187 exportForm = kSecFormatBSAFE; 188 } 189 else if(!strcmp("pkcs7", optarg)) { 190 exportForm = kSecFormatPKCS7; 191 } 192 else if(!strcmp("pkcs8", optarg)) { 193 exportForm = kSecFormatWrappedPKCS8; 194 } 195 else if(!strcmp("pkcs12", optarg)) { 196 exportForm = kSecFormatPKCS12; 197 } 198 else if(!strcmp("pemseq", optarg)) { 199 exportForm = kSecFormatPEMSequence; 200 } 201 else { 202 usage(argv); 203 } 204 break; 205 case 'p': 206 pemEncode = true; 207 break; 208 case 'o': 209 outFile = optarg; 210 break; 211 case 'z': 212 passphrase = CFStringCreateWithCString(NULL, optarg, 213 kCFStringEncodingASCII); 214 break; 215 case 'Z': 216 securePassphrase = true; 217 break; 218 case 'w': 219 wrapPrivKeys = true; 220 break; 221 case 'q': 222 quiet = true; 223 break; 224 case '?': 225 case 'h': 226 default: 227 usage(argv); 228 } 229 230 } 231 if(optind != argc) { 232 /* getopt does not return '?' */ 233 usage(argv); 234 } 235 if(wrapPrivKeys) { 236 switch(exportForm) { 237 case kSecFormatOpenSSL: 238 case kSecFormatUnknown: // i.e., use default 239 exportForm = kSecFormatWrappedOpenSSL; 240 break; 241 case kSecFormatSSH: 242 exportForm = kSecFormatWrappedSSH; 243 break; 244 case kSecFormatSSHv2: 245 /* there is no wrappedSSHv2 */ 246 exportForm = kSecFormatWrappedOpenSSL; 247 break; 248 case kSecFormatWrappedPKCS8: 249 /* proceed */ 250 break; 251 default: 252 printf("Don't know how to wrap in specified format/type.\n"); 253 exit(1); 254 } 255 } 256 257 /* gather items */ 258 CFMutableArrayRef exportItems = CFArrayCreateMutable(NULL, 0, 259 &kCFTypeArrayCallBacks); 260 switch(itemSpec) { 261 case IS_Certs: 262 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems); 263 if(ortn) { 264 exit(1); 265 } 266 break; 267 268 case IS_PrivKeys: 269 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems); 270 if(ortn) { 271 exit(1); 272 } 273 break; 274 275 case IS_PubKeys: 276 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems); 277 if(ortn) { 278 exit(1); 279 } 280 break; 281 282 case IS_AllKeys: 283 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems); 284 if(ortn) { 285 exit(1); 286 } 287 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems); 288 if(ortn) { 289 exit(1); 290 } 291 break; 292 293 case IS_All: 294 /* No public keys here - PKCS12 doesn't support them */ 295 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems); 296 if(ortn) { 297 exit(1); 298 } 299 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems); 300 if(ortn) { 301 exit(1); 302 } 303 break; 304 305 case IS_Identities: 306 ortn = addIdentities(kcRef, exportItems); 307 if(ortn) { 308 exit(1); 309 } 310 break; 311 default: 312 printf("Huh? Bogus itemSpec!\n"); 313 exit(1); 314 } 315 316 CFIndex numItems = CFArrayGetCount(exportItems); 317 318 if(exportForm == kSecFormatUnknown) { 319 /* Use default export format per set of items */ 320 if(numItems > 1) { 321 exportForm = kSecFormatPEMSequence; 322 } 323 else { 324 exportForm = kSecFormatOpenSSL; 325 } 326 } 327 uint32 expFlags = 0; // SecItemImportExportFlags 328 if(pemEncode) { 329 expFlags |= kSecItemPemArmour; 330 } 331 332 /* optional key related arguments */ 333 SecKeyImportExportParameters keyParams; 334 SecKeyImportExportParameters *keyParamPtr = NULL; 335 if((passphrase != NULL) || securePassphrase) { 336 memset(&keyParams, 0, sizeof(keyParams)); 337 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; 338 if(securePassphrase) { 339 /* give this precedence */ 340 keyParams.flags |= kSecKeySecurePassphrase; 341 } 342 else { 343 keyParams.passphrase = passphrase; // may be NULL 344 } 345 keyParamPtr = &keyParams; 346 } 347 348 /* GO */ 349 CFDataRef outData = NULL; 350 ortn = SecKeychainItemExport(exportItems, exportForm, expFlags, keyParamPtr, 351 &outData); 352 if(ortn) { 353 cssmPerror("SecKeychainItemExport", ortn); 354 exit(1); 355 } 356 357 unsigned len = CFDataGetLength(outData); 358 if(outFile) { 359 int rtn = writeFile(outFile, CFDataGetBytePtr(outData), len); 360 if(rtn == 0) { 361 if(!quiet) { 362 printf("...%u bytes written to %s\n", len, outFile); 363 } 364 } 365 else { 366 printf("***Error writing to %s\n", outFile); 367 } 368 } 369 else { 370 int irtn = write(STDOUT_FILENO, CFDataGetBytePtr(outData), len); 371 if(irtn != (int)len) { 372 perror("write"); 373 } 374 } 375 if(!quiet) { 376 fprintf(stderr, "\n%u items exported.\n", (unsigned)numItems); 377 } 378 if(exportItems) { 379 /* FIXME this in conjunction with the release of the KC crashes */ 380 CFRelease(exportItems); 381 } 382 if(passphrase) { 383 CFRelease(passphrase); 384 } 385 if(outData) { 386 CFRelease(outData); 387 } 388 if(kcRef) { 389 CFRelease(kcRef); 390 } 391 return 0; 392} 393