1/* 2 * p12ImportExport.cpp - high-level libnsspkcs12 exerciser 3 */ 4 5#include <security_pkcs12/SecPkcs12.h> 6#include <security_cdsa_utils/cuFileIo.h> 7#include <Security/Security.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <security_cdsa_utilities/KeySchema.h> 11#include <security_cdsa_utils/cuCdsaUtils.h> 12#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 13#include "p12GetPassKey.h" 14 15static void printOsError( 16 const char *op, 17 OSStatus ortn) 18{ 19 char *errStr = NULL; 20 switch(ortn) { 21 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: 22 errStr = "CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA"; break; 23 case CSSMERR_DL_DATASTORE_DOESNOT_EXIST: 24 errStr = "CSSMERR_DL_DATASTORE_DOESNOT_EXIST"; break; 25 case errSecDuplicateItem: 26 errStr = "errSecDuplicateItem"; break; 27 case errSecNotAvailable: 28 errStr = "errSecNotAvailable"; break; 29 case errSecAuthFailed: 30 errStr = "errSecAuthFailed"; break; 31 case errSecItemNotFound: 32 errStr = "errSecItemNotFound"; break; 33 case errSecInvalidItemRef: 34 errStr = "errSecInvalidItemRef"; break; 35 default: 36 break; 37 } 38 if(errStr) { 39 printf("%s returned %s\n", op, errStr); 40 } 41 else { 42 printf("%s returned %d\n", op, (int)ortn); 43 } 44} 45 46/* 47 * For now we assume "import everything" 48 */ 49int p12Import( 50 const char *pfxFile, 51 const char *kcName, 52 CFStringRef pwd, // explicit passphrase, mutually exclusive with... 53 bool usePassKey, // use SECURE_PASSPHRASE key 54 const char *kcPwd) // optional 55{ 56 OSStatus ortn; 57 unsigned char *pfx; 58 unsigned pfxLen; 59 CSSM_KEY passKey; 60 61 /* get the PFX */ 62 if(readFile(pfxFile, &pfx, &pfxLen)) { 63 printf("***Error reading pfx from %s. Aborting.\n", pfxFile); 64 return 1; 65 } 66 CFDataRef cfd = CFDataCreate(NULL, pfx, pfxLen); 67 68 /* import to keychain specified by kcName */ 69 SecKeychainRef kcRef = NULL; 70 ortn = SecKeychainOpen(kcName, &kcRef); 71 if(ortn) { 72 printOsError("SecKeychainOpen", ortn); 73 return ortn; 74 } 75 76 if(kcPwd) { 77 ortn = SecKeychainUnlock(kcRef, strlen(kcPwd), (void *)kcPwd, true); 78 if(ortn) { 79 printOsError("SecKeychainUnlock", ortn); 80 } 81 } 82 83 /* set up a pkcs12 coder for import */ 84 SecPkcs12CoderRef coder; 85 ortn = SecPkcs12CoderCreate(&coder); 86 if(ortn) { 87 printOsError("SecPkcs12CoderCreate", ortn); 88 return ortn; 89 } 90 91 ortn = SecPkcs12SetKeychain(coder, kcRef); 92 if(ortn) { 93 printOsError("SecPkcs12SetKeychain", ortn); 94 return ortn; 95 } 96 97 if(usePassKey) { 98 CSSM_CSP_HANDLE cspHand; 99 ortn = SecKeychainGetCSPHandle(kcRef, &cspHand); 100 if(ortn) { 101 printOsError("SecPkcs12SetKeychain", ortn); 102 return ortn; 103 } 104 ortn = p12GetPassKey(cspHand, GPK_Decode, false, &passKey); 105 if(ortn) { 106 return ortn; 107 } 108 ortn = SecPkcs12SetMACPassKey(coder, &passKey); 109 if(ortn) { 110 printOsError("SecPkcs12SetMACPassKey", ortn); 111 return ortn; 112 } 113 } 114 else { 115 ortn = SecPkcs12SetMACPassphrase(coder, pwd); 116 if(ortn) { 117 printOsError("SecPkcs12SetMACPassphrase", ortn); 118 return ortn; 119 } 120 } 121 122 /* 123 * For now we assume "import everything" 124 */ 125 ortn = SecPkcs12SetImportToKeychain(coder, 126 kSecImportCertificates | 127 kSecImportCRLs | 128 kSecImportKeys); 129 if(ortn) { 130 printOsError("SecPkcs12SetImportFromKeychain", ortn); 131 return ortn; 132 } 133 134 /* Go! */ 135 ortn = SecPkcs12Decode(coder, cfd); 136 if(ortn) { 137 printOsError("SecPkcs12Decode", ortn); 138 return ortn; 139 } 140 141 /* report how many of each item got imported */ 142 CFIndex num; 143 SecPkcs12CertificateCount(coder, &num); 144 printf("...%d certs imported\n", (int)num); 145 SecPkcs12CrlCount(coder, &num); 146 printf("...%d CRLs imported\n", (int)num); 147 SecPkcs12PrivateKeyCount(coder, &num); 148 printf("...%d private keys imported\n", (int)num); 149 150 SecPkcs12CoderRelease(coder); 151 CFRelease(cfd); 152 free(pfx); // mallocd by readFile() 153 return 0; 154} 155 156/* 157 * Use the kludge from hell to get the name-as-int form of a specified 158 * "known" name-as-string for the Key Schema. 159 */ 160OSStatus attrNameToInt( 161 const char *name, 162 uint32 *attrInt) 163{ 164 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList = 165 KeySchema::KeySchemaAttributeList; 166 unsigned numAttrs = KeySchema::KeySchemaAttributeCount; 167 for(unsigned dex=0; dex<numAttrs; dex++) { 168 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *info = &attrList[dex]; 169 if(!strcmp(name, info->AttributeName)) { 170 *attrInt = info->AttributeId; 171 return noErr; 172 } 173 } 174 return paramErr; 175} 176 177static int p12AddExportedItem( 178 SecKeychainItemRef item, 179 CFMutableArrayRef itemArray, 180 bool noPrompt) 181{ 182 if(noPrompt) { 183 CFArrayAppendValue(itemArray, item); 184 return 1; 185 } 186 187 CFTypeID itemId = CFGetTypeID(item); 188 OSStatus ortn; 189 190 /* the printable name attr */ 191 UInt32 nameAttr = 0; 192 char *itemClass = ""; 193 if(itemId == SecCertificateGetTypeID()) { 194 itemClass = "Certificate"; 195 nameAttr = kSecLabelItemAttr; 196 } 197 else if(itemId == SecKeyGetTypeID()) { 198 itemClass = "Private Key"; 199 ortn = attrNameToInt("PrintName", &nameAttr); 200 if(ortn) { 201 /* out of sync with Sec layer? With KeySchema? */ 202 printf("warning: attrNameToInt failure\n"); 203 return 0; 204 } 205 } 206 else { 207 /* we don't know how to deal with this */ 208 printf("p12AddExportedItem: internal screwup\n"); 209 return 0; 210 } 211 212 /* get the printable name attr */ 213 SecKeychainAttributeInfo attrInfo; 214 attrInfo.count = 1; 215 attrInfo.tag = &nameAttr; 216 attrInfo.format = NULL; // ??? 217 218 /* FIXME header says this is an IN/OUT param, but it's not */ 219 SecKeychainAttributeList *attrList = NULL; 220 221 ortn = SecKeychainItemCopyAttributesAndData( 222 item, 223 &attrInfo, 224 NULL, // itemClass 225 &attrList, 226 NULL, // don't need the data 227 NULL); 228 if(ortn) { 229 printOsError("SecKeychainItemCopyAttributesAndData", ortn); 230 return 0; 231 } 232 if(attrList->count != 1) { 233 printf("***Unexpected attribute count (%u) for %s\n", 234 (unsigned)attrList->count, itemClass); 235 return 0; 236 } 237 SecKeychainAttribute *attr = attrList->attr; 238 239 /* it's a UTF8 string: use CFString to convert to C ASCII string */ 240 CFStringRef cfStr = CFStringCreateWithBytes(NULL, 241 (UInt8 *)attr->data, attr->length, 242 kCFStringEncodingUTF8, false); 243 SecKeychainItemFreeAttributesAndData(attrList, NULL); 244 if(cfStr == NULL) { 245 printf("***Error converting %s name to UTF CFSTring.\n", 246 itemClass); 247 return 0; 248 } 249 250 CFIndex strLen = CFStringGetLength(cfStr); 251 char *printName = (char *)malloc(strLen + 1); 252 if(!CFStringGetCString(cfStr, printName, strLen + 1, kCFStringEncodingASCII)) { 253 printf("***Error converting %s name to ASCII\n", itemClass); 254 return 0; 255 } 256 CFRelease(cfStr); 257 258 char *aliasCStr = NULL; 259 if((itemId == SecCertificateGetTypeID())) { 260 /* the alias attr, for cert email */ 261 CFStringRef aliasCFStr = NULL; 262 nameAttr = kSecAlias; 263 attrInfo.count = 1; 264 attrInfo.tag = &nameAttr; 265 attrInfo.format = NULL; // ??? 266 attrList = NULL; 267 268 ortn = SecKeychainItemCopyAttributesAndData( 269 item, 270 &attrInfo, 271 NULL, // itemClass 272 &attrList, 273 NULL, // don't need the data 274 NULL); 275 if(ortn) { 276 printOsError("SecKeychainItemCopyAttributesAndData", ortn); 277 return 0; 278 } 279 if(attrList->count != 1) { 280 printf("***Unexpected attribute count (%u) for Alias\n", 281 (unsigned)attrList->count); 282 return 0; 283 } 284 attr = attrList->attr; 285 286 /* it's a UTF8 string: use CFString to convert to C ASCII string */ 287 aliasCFStr = CFStringCreateWithBytes(NULL, 288 (UInt8 *)attr->data, attr->length, 289 kCFStringEncodingUTF8, false); 290 if(aliasCFStr == NULL) { 291 printf("***Error converting Alias name to UTF CFSTring.\n"); 292 return 0; 293 } 294 295 strLen = CFStringGetLength(aliasCFStr); 296 aliasCStr = (char *)malloc(strLen + 1); 297 if(!CFStringGetCString(aliasCFStr, aliasCStr, strLen + 1, 298 kCFStringEncodingASCII)) { 299 printf("***Error converting Alias name to ASCII\n"); 300 return 0; 301 } 302 CFRelease(aliasCFStr); 303 } 304 305 int ourRtn = 0; 306 fpurge(stdin); 307 printf("Found %s\n", itemClass); 308 printf(" printable name : %s\n", printName); 309 if(aliasCStr != NULL) { 310 printf(" alias : %s\n", aliasCStr); 311 } 312 printf("Export (y/anything)? "); 313 char c = getchar(); 314 if(c == 'y') { 315 CFArrayAppendValue(itemArray, item); 316 ourRtn = 1; 317 } 318 free(printName); 319 if(aliasCStr) { 320 free(aliasCStr); 321 } 322 return ourRtn; 323} 324 325int p12Export( 326 const char *pfxFile, 327 const char *kcName, 328 CFStringRef pwd, // explicit passphrase, mutually exclusive with... 329 bool usePassKey, // use SECURE_PASSPHRASE key 330 const char *kcPwd, // optional 331 bool noPrompt) // true --> export all 332{ 333 OSStatus ortn; 334 CSSM_KEY passKey; 335 336 /* set up a pkcs12 coder for export */ 337 SecPkcs12CoderRef coder; 338 ortn = SecPkcs12CoderCreate(&coder); 339 if(ortn) { 340 printOsError("SecPkcs12CoderCreate", ortn); 341 return ortn; 342 } 343 344 /* 345 �* Since we're not providing the SecPkcs12CoderRef with a 346 * keychain, we have to provide the CSPDL handle 347 */ 348 CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_FALSE); 349 if(cspHand == 0) { 350 printf("***Error attaching to CSPDL. Aborting.\n"); 351 return 1; 352 } 353 354 if(usePassKey) { 355 ortn = p12GetPassKey(cspHand, GPK_Encode, false, &passKey); 356 if(ortn) { 357 return ortn; 358 } 359 ortn = SecPkcs12SetMACPassKey(coder, &passKey); 360 if(ortn) { 361 printOsError("SecPkcs12SetMACPassKey", ortn); 362 return ortn; 363 } 364 } 365 else { 366 ortn = SecPkcs12SetMACPassphrase(coder, pwd); 367 if(ortn) { 368 printOsError("SecPkcs12SetMACPassphrase", ortn); 369 return ortn; 370 } 371 } 372 373 ortn = SecPkcs12SetCspHandle(coder, cspHand); 374 if(ortn) { 375 printOsError("SecPkcs12SetCspHandle", ortn); 376 return ortn; 377 } 378 379 /* the array of things we want to export */ 380 CFMutableArrayRef items = CFArrayCreateMutable(NULL, 0, NULL); 381 382 /* export from keychain specified by kcName */ 383 SecKeychainRef kcRef = NULL; 384 ortn = SecKeychainOpen(kcName, &kcRef); 385 if(ortn) { 386 printOsError("SecKeychainOpen", ortn); 387 return ortn; 388 } 389 390 if(kcPwd) { 391 ortn = SecKeychainUnlock(kcRef, strlen(kcPwd), (void *)kcPwd, true); 392 if(ortn) { 393 printOsError("SecKeychainUnlock", ortn); 394 } 395 } 396 397 /* 398 * Prompt user for each known item - it would be nice if we 399 * could search for anything, eh? 400 * Certs first... 401 */ 402 SecKeychainSearchRef srchRef; 403 ortn = SecKeychainSearchCreateFromAttributes(kcRef, 404 kSecCertificateItemClass, 405 NULL, // no attrs 406 &srchRef); 407 if(ortn) { 408 printOsError("SecKeychainSearchCreateFromAttributes", ortn); 409 return ortn; 410 } 411 int exported = 0; 412 for(;;) { 413 SecKeychainItemRef certRef; 414 ortn = SecKeychainSearchCopyNext(srchRef, &certRef); 415 if(ortn) { 416 break; 417 } 418 exported += p12AddExportedItem(certRef, items, noPrompt); 419 } 420 CFRelease(srchRef); 421 422 /* now private keys */ 423 ortn = SecKeychainSearchCreateFromAttributes(kcRef, 424 CSSM_DL_DB_RECORD_PRIVATE_KEY, // undocumented 425 NULL, // no attrs 426 &srchRef); 427 if(ortn) { 428 printOsError("SecKeychainSearchCreateFromAttributes", ortn); 429 return ortn; 430 } 431 for(;;) { 432 SecKeychainItemRef keyRef; 433 ortn = SecKeychainSearchCopyNext(srchRef, &keyRef); 434 if(ortn) { 435 break; 436 } 437 exported += p12AddExportedItem(keyRef, items, noPrompt); 438 } 439 440 if(exported == 0) { 441 printf("...Hmmm, no items to export. Done.\n"); 442 return 0; 443 } 444 ortn = SecPkcs12ExportKeychainItems(coder, items); 445 if(ortn) { 446 printOsError("SecPkcs12ExportKeychainItems", ortn); 447 return ortn; 448 } 449 450 /* go */ 451 CFDataRef pfx; 452 ortn = SecPkcs12Encode(coder, &pfx); 453 if(ortn) { 454 printOsError("SecPkcs12ExportKeychainItems", ortn); 455 return ortn; 456 } 457 458 if(writeFile(pfxFile, CFDataGetBytePtr(pfx), 459 CFDataGetLength(pfx))) { 460 printf("***Error writing pfx to %s\n", pfxFile); 461 return 1; 462 } 463 printf("...%u items exported; %ld bytes written to %s\n", 464 exported, CFDataGetLength(pfx), pfxFile); 465 466 /* cleanup */ 467 SecPkcs12CoderRelease(coder); 468 CFRelease(pfx); 469 CFRelease(srchRef); 470 CFRelease(kcRef); 471 return 0; 472} 473