X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/p12/p12ImportExport.cpp diff --git a/SecurityTests/clxutils/p12/p12ImportExport.cpp b/SecurityTests/clxutils/p12/p12ImportExport.cpp new file mode 100644 index 00000000..13c52af5 --- /dev/null +++ b/SecurityTests/clxutils/p12/p12ImportExport.cpp @@ -0,0 +1,472 @@ +/* + * p12ImportExport.cpp - high-level libnsspkcs12 exerciser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "p12GetPassKey.h" + +static void printOsError( + const char *op, + OSStatus ortn) +{ + char *errStr = NULL; + switch(ortn) { + case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: + errStr = "CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA"; break; + case CSSMERR_DL_DATASTORE_DOESNOT_EXIST: + errStr = "CSSMERR_DL_DATASTORE_DOESNOT_EXIST"; break; + case errSecDuplicateItem: + errStr = "errSecDuplicateItem"; break; + case errSecNotAvailable: + errStr = "errSecNotAvailable"; break; + case errSecAuthFailed: + errStr = "errSecAuthFailed"; break; + case errSecItemNotFound: + errStr = "errSecItemNotFound"; break; + case errSecInvalidItemRef: + errStr = "errSecInvalidItemRef"; break; + default: + break; + } + if(errStr) { + printf("%s returned %s\n", op, errStr); + } + else { + printf("%s returned %d\n", op, (int)ortn); + } +} + +/* + * For now we assume "import everything" + */ +int p12Import( + const char *pfxFile, + const char *kcName, + CFStringRef pwd, // explicit passphrase, mutually exclusive with... + bool usePassKey, // use SECURE_PASSPHRASE key + const char *kcPwd) // optional +{ + OSStatus ortn; + unsigned char *pfx; + unsigned pfxLen; + CSSM_KEY passKey; + + /* get the PFX */ + if(readFile(pfxFile, &pfx, &pfxLen)) { + printf("***Error reading pfx from %s. Aborting.\n", pfxFile); + return 1; + } + CFDataRef cfd = CFDataCreate(NULL, pfx, pfxLen); + + /* import to keychain specified by kcName */ + SecKeychainRef kcRef = NULL; + ortn = SecKeychainOpen(kcName, &kcRef); + if(ortn) { + printOsError("SecKeychainOpen", ortn); + return ortn; + } + + if(kcPwd) { + ortn = SecKeychainUnlock(kcRef, strlen(kcPwd), (void *)kcPwd, true); + if(ortn) { + printOsError("SecKeychainUnlock", ortn); + } + } + + /* set up a pkcs12 coder for import */ + SecPkcs12CoderRef coder; + ortn = SecPkcs12CoderCreate(&coder); + if(ortn) { + printOsError("SecPkcs12CoderCreate", ortn); + return ortn; + } + + ortn = SecPkcs12SetKeychain(coder, kcRef); + if(ortn) { + printOsError("SecPkcs12SetKeychain", ortn); + return ortn; + } + + if(usePassKey) { + CSSM_CSP_HANDLE cspHand; + ortn = SecKeychainGetCSPHandle(kcRef, &cspHand); + if(ortn) { + printOsError("SecPkcs12SetKeychain", ortn); + return ortn; + } + ortn = p12GetPassKey(cspHand, GPK_Decode, false, &passKey); + if(ortn) { + return ortn; + } + ortn = SecPkcs12SetMACPassKey(coder, &passKey); + if(ortn) { + printOsError("SecPkcs12SetMACPassKey", ortn); + return ortn; + } + } + else { + ortn = SecPkcs12SetMACPassphrase(coder, pwd); + if(ortn) { + printOsError("SecPkcs12SetMACPassphrase", ortn); + return ortn; + } + } + + /* + * For now we assume "import everything" + */ + ortn = SecPkcs12SetImportToKeychain(coder, + kSecImportCertificates | + kSecImportCRLs | + kSecImportKeys); + if(ortn) { + printOsError("SecPkcs12SetImportFromKeychain", ortn); + return ortn; + } + + /* Go! */ + ortn = SecPkcs12Decode(coder, cfd); + if(ortn) { + printOsError("SecPkcs12Decode", ortn); + return ortn; + } + + /* report how many of each item got imported */ + CFIndex num; + SecPkcs12CertificateCount(coder, &num); + printf("...%d certs imported\n", (int)num); + SecPkcs12CrlCount(coder, &num); + printf("...%d CRLs imported\n", (int)num); + SecPkcs12PrivateKeyCount(coder, &num); + printf("...%d private keys imported\n", (int)num); + + SecPkcs12CoderRelease(coder); + CFRelease(cfd); + free(pfx); // mallocd by readFile() + return 0; +} + +/* + * Use the kludge from hell to get the name-as-int form of a specified + * "known" name-as-string for the Key Schema. + */ +OSStatus attrNameToInt( + const char *name, + uint32 *attrInt) +{ + const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList = + KeySchema::KeySchemaAttributeList; + unsigned numAttrs = KeySchema::KeySchemaAttributeCount; + for(unsigned dex=0; dexAttributeName)) { + *attrInt = info->AttributeId; + return noErr; + } + } + return paramErr; +} + +static int p12AddExportedItem( + SecKeychainItemRef item, + CFMutableArrayRef itemArray, + bool noPrompt) +{ + if(noPrompt) { + CFArrayAppendValue(itemArray, item); + return 1; + } + + CFTypeID itemId = CFGetTypeID(item); + OSStatus ortn; + + /* the printable name attr */ + UInt32 nameAttr = 0; + char *itemClass = ""; + if(itemId == SecCertificateGetTypeID()) { + itemClass = "Certificate"; + nameAttr = kSecLabelItemAttr; + } + else if(itemId == SecKeyGetTypeID()) { + itemClass = "Private Key"; + ortn = attrNameToInt("PrintName", &nameAttr); + if(ortn) { + /* out of sync with Sec layer? With KeySchema? */ + printf("warning: attrNameToInt failure\n"); + return 0; + } + } + else { + /* we don't know how to deal with this */ + printf("p12AddExportedItem: internal screwup\n"); + return 0; + } + + /* get the printable name attr */ + SecKeychainAttributeInfo attrInfo; + attrInfo.count = 1; + attrInfo.tag = &nameAttr; + attrInfo.format = NULL; // ??? + + /* FIXME header says this is an IN/OUT param, but it's not */ + SecKeychainAttributeList *attrList = NULL; + + ortn = SecKeychainItemCopyAttributesAndData( + item, + &attrInfo, + NULL, // itemClass + &attrList, + NULL, // don't need the data + NULL); + if(ortn) { + printOsError("SecKeychainItemCopyAttributesAndData", ortn); + return 0; + } + if(attrList->count != 1) { + printf("***Unexpected attribute count (%u) for %s\n", + (unsigned)attrList->count, itemClass); + return 0; + } + SecKeychainAttribute *attr = attrList->attr; + + /* it's a UTF8 string: use CFString to convert to C ASCII string */ + CFStringRef cfStr = CFStringCreateWithBytes(NULL, + (UInt8 *)attr->data, attr->length, + kCFStringEncodingUTF8, false); + SecKeychainItemFreeAttributesAndData(attrList, NULL); + if(cfStr == NULL) { + printf("***Error converting %s name to UTF CFSTring.\n", + itemClass); + return 0; + } + + CFIndex strLen = CFStringGetLength(cfStr); + char *printName = (char *)malloc(strLen + 1); + if(!CFStringGetCString(cfStr, printName, strLen + 1, kCFStringEncodingASCII)) { + printf("***Error converting %s name to ASCII\n", itemClass); + return 0; + } + CFRelease(cfStr); + + char *aliasCStr = NULL; + if((itemId == SecCertificateGetTypeID())) { + /* the alias attr, for cert email */ + CFStringRef aliasCFStr = NULL; + nameAttr = kSecAlias; + attrInfo.count = 1; + attrInfo.tag = &nameAttr; + attrInfo.format = NULL; // ??? + attrList = NULL; + + ortn = SecKeychainItemCopyAttributesAndData( + item, + &attrInfo, + NULL, // itemClass + &attrList, + NULL, // don't need the data + NULL); + if(ortn) { + printOsError("SecKeychainItemCopyAttributesAndData", ortn); + return 0; + } + if(attrList->count != 1) { + printf("***Unexpected attribute count (%u) for Alias\n", + (unsigned)attrList->count); + return 0; + } + attr = attrList->attr; + + /* it's a UTF8 string: use CFString to convert to C ASCII string */ + aliasCFStr = CFStringCreateWithBytes(NULL, + (UInt8 *)attr->data, attr->length, + kCFStringEncodingUTF8, false); + if(aliasCFStr == NULL) { + printf("***Error converting Alias name to UTF CFSTring.\n"); + return 0; + } + + strLen = CFStringGetLength(aliasCFStr); + aliasCStr = (char *)malloc(strLen + 1); + if(!CFStringGetCString(aliasCFStr, aliasCStr, strLen + 1, + kCFStringEncodingASCII)) { + printf("***Error converting Alias name to ASCII\n"); + return 0; + } + CFRelease(aliasCFStr); + } + + int ourRtn = 0; + fpurge(stdin); + printf("Found %s\n", itemClass); + printf(" printable name : %s\n", printName); + if(aliasCStr != NULL) { + printf(" alias : %s\n", aliasCStr); + } + printf("Export (y/anything)? "); + char c = getchar(); + if(c == 'y') { + CFArrayAppendValue(itemArray, item); + ourRtn = 1; + } + free(printName); + if(aliasCStr) { + free(aliasCStr); + } + return ourRtn; +} + +int p12Export( + const char *pfxFile, + const char *kcName, + CFStringRef pwd, // explicit passphrase, mutually exclusive with... + bool usePassKey, // use SECURE_PASSPHRASE key + const char *kcPwd, // optional + bool noPrompt) // true --> export all +{ + OSStatus ortn; + CSSM_KEY passKey; + + /* set up a pkcs12 coder for export */ + SecPkcs12CoderRef coder; + ortn = SecPkcs12CoderCreate(&coder); + if(ortn) { + printOsError("SecPkcs12CoderCreate", ortn); + return ortn; + } + + /* + Ê* Since we're not providing the SecPkcs12CoderRef with a + * keychain, we have to provide the CSPDL handle + */ + CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_FALSE); + if(cspHand == 0) { + printf("***Error attaching to CSPDL. Aborting.\n"); + return 1; + } + + if(usePassKey) { + ortn = p12GetPassKey(cspHand, GPK_Encode, false, &passKey); + if(ortn) { + return ortn; + } + ortn = SecPkcs12SetMACPassKey(coder, &passKey); + if(ortn) { + printOsError("SecPkcs12SetMACPassKey", ortn); + return ortn; + } + } + else { + ortn = SecPkcs12SetMACPassphrase(coder, pwd); + if(ortn) { + printOsError("SecPkcs12SetMACPassphrase", ortn); + return ortn; + } + } + + ortn = SecPkcs12SetCspHandle(coder, cspHand); + if(ortn) { + printOsError("SecPkcs12SetCspHandle", ortn); + return ortn; + } + + /* the array of things we want to export */ + CFMutableArrayRef items = CFArrayCreateMutable(NULL, 0, NULL); + + /* export from keychain specified by kcName */ + SecKeychainRef kcRef = NULL; + ortn = SecKeychainOpen(kcName, &kcRef); + if(ortn) { + printOsError("SecKeychainOpen", ortn); + return ortn; + } + + if(kcPwd) { + ortn = SecKeychainUnlock(kcRef, strlen(kcPwd), (void *)kcPwd, true); + if(ortn) { + printOsError("SecKeychainUnlock", ortn); + } + } + + /* + * Prompt user for each known item - it would be nice if we + * could search for anything, eh? + * Certs first... + */ + SecKeychainSearchRef srchRef; + ortn = SecKeychainSearchCreateFromAttributes(kcRef, + kSecCertificateItemClass, + NULL, // no attrs + &srchRef); + if(ortn) { + printOsError("SecKeychainSearchCreateFromAttributes", ortn); + return ortn; + } + int exported = 0; + for(;;) { + SecKeychainItemRef certRef; + ortn = SecKeychainSearchCopyNext(srchRef, &certRef); + if(ortn) { + break; + } + exported += p12AddExportedItem(certRef, items, noPrompt); + } + CFRelease(srchRef); + + /* now private keys */ + ortn = SecKeychainSearchCreateFromAttributes(kcRef, + CSSM_DL_DB_RECORD_PRIVATE_KEY, // undocumented + NULL, // no attrs + &srchRef); + if(ortn) { + printOsError("SecKeychainSearchCreateFromAttributes", ortn); + return ortn; + } + for(;;) { + SecKeychainItemRef keyRef; + ortn = SecKeychainSearchCopyNext(srchRef, &keyRef); + if(ortn) { + break; + } + exported += p12AddExportedItem(keyRef, items, noPrompt); + } + + if(exported == 0) { + printf("...Hmmm, no items to export. Done.\n"); + return 0; + } + ortn = SecPkcs12ExportKeychainItems(coder, items); + if(ortn) { + printOsError("SecPkcs12ExportKeychainItems", ortn); + return ortn; + } + + /* go */ + CFDataRef pfx; + ortn = SecPkcs12Encode(coder, &pfx); + if(ortn) { + printOsError("SecPkcs12ExportKeychainItems", ortn); + return ortn; + } + + if(writeFile(pfxFile, CFDataGetBytePtr(pfx), + CFDataGetLength(pfx))) { + printf("***Error writing pfx to %s\n", pfxFile); + return 1; + } + printf("...%u items exported; %ld bytes written to %s\n", + exported, CFDataGetLength(pfx), pfxFile); + + /* cleanup */ + SecPkcs12CoderRelease(coder); + CFRelease(pfx); + CFRelease(srchRef); + CFRelease(kcRef); + return 0; +}