1/* 2 * Copyright (c) 2003-2004 Apple Computer, 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 43typedef 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 */ 57static 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 */ 98static 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 136static 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 } 293loser: 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 306int 307keychain_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} 442