--- /dev/null
+/*
+ * p12Reencode - take a p12 PFX, decode and reencode
+ */
+#include <Security/SecImportExport.h>\ 1
+#include <Security/Security.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <utilLib/common.h>
+
+static void usage(char **argv)
+{
+ printf("Usage: %s pfx password keychain1 keychain2 [l=loops] [q(uiet)] "
+ "[v(erbose)]\n", argv[0]);
+ exit(1);
+}
+
+
+#define WRITE_BLOBS 0
+#if WRITE_BLOBS
+static void writeBlobs(CFDataRef pfx1, CFDataRef pfx2)
+{
+ writeFile("pfx1.der", CFDataGetBytePtr(pfx1), CFDataGetLength(pfx1));
+ writeFile("pfx2.der", CFDataGetBytePtr(pfx2), CFDataGetLength(pfx2));
+ printf("...wrote %u bytes to pfx1.der, %u bytes to pfx2.der\n",
+ CFDataGetLength(pfx1), CFDataGetLength(pfx2));
+}
+#else
+#define writeBlobs(p1, p2)
+#endif
+
+#if 0
+/* Not possible using import/export API */
+/* compare attrs, all of which are optional */
+static int compareAttrs(
+ CFStringRef refFriendlyName,
+ CFDataRef refLocalKeyId,
+ CFStringRef testFriendlyName,
+ CFDataRef testLocalKeyId,
+ char *itemType,
+ CSSM_BOOL quiet)
+{
+ if(refFriendlyName == NULL) {
+ if(testFriendlyName != NULL) {
+ printf("****s refFriendlyName NULL, testFriendlyName "
+ "non-NULL\n", itemType);
+ return testError(quiet);
+ }
+ }
+ else {
+ CFComparisonResult res = CFStringCompare(refFriendlyName,
+ testFriendlyName, 0);
+ if(res != kCFCompareEqualTo) {
+ printf("***%s friendlyName Miscompare\n", itemType);
+ return testError(quiet);
+ }
+ }
+
+ if(refLocalKeyId == NULL) {
+ if(testLocalKeyId != NULL) {
+ printf("****s refLocalKeyId NULL, testLocalKeyId "
+ "non-NULL\n", itemType);
+ return testError(quiet);
+ }
+ }
+ else {
+ if(compareCfData(refLocalKeyId, testLocalKeyId)) {
+ printf("***%s localKeyId Miscompare\n", itemType);
+ return testError(quiet);
+ }
+ }
+
+ /* release the attrs */
+ if(refFriendlyName) {
+ CFRelease(refFriendlyName);
+ }
+ if(refLocalKeyId) {
+ CFRelease(refLocalKeyId);
+ }
+ if(testFriendlyName) {
+ CFRelease(testFriendlyName);
+ }
+ if(testLocalKeyId) {
+ CFRelease(testLocalKeyId);
+ }
+ return 0;
+}
+#endif
+
+static void setUpKeyParams(
+ SecKeyImportExportParameters &keyParams,
+ CFStringRef pwd)
+{
+ memset(&keyParams, 0, sizeof(keyParams));
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ keyParams.passphrase = pwd;
+}
+
+/*
+ * Basic import/export: convert between CFArray of keychain items and a CFDataRef
+ */
+static OSStatus p12Import(
+ CFDataRef pfx,
+ CFStringRef pwd,
+ SecKeychainRef kcRef,
+ CFArrayRef *outArray)
+{
+ SecKeyImportExportParameters keyParams;
+ setUpKeyParams(keyParams, pwd);
+ OSStatus ortn;
+ SecExternalFormat format = kSecFormatPKCS12;
+
+ ortn = SecKeychainItemImport(pfx, NULL, &format, NULL, 0, &keyParams,
+ kcRef, outArray);
+ if(ortn) {
+ cssmPerror("SecKeychainItemImport", ortn);
+ }
+ return ortn;
+}
+
+static OSStatus p12Export(
+ CFArrayRef inArray,
+ CFStringRef pwd,
+ CFDataRef *pfx)
+{
+ SecKeyImportExportParameters keyParams;
+ setUpKeyParams(keyParams, pwd);
+ OSStatus ortn;
+
+ ortn = SecKeychainItemExport(inArray, kSecFormatPKCS12, 0, &keyParams, pfx);
+ if(ortn) {
+ cssmPerror("SecKeychainItemExport", ortn);
+ }
+ return ortn;
+}
+
+/*
+ * Compare two CFArrayRefs containing various items, subsequent to decode. Returns
+ * nonzero if they differ.
+ *
+ * As of April 9 2004, we do NOT see CRLs so we don't compare them. I think
+ * we need a SecCRLRef...
+ */
+static int compareDecodedArrays(
+ CFArrayRef refArray,
+ CFArrayRef testArray,
+ CSSM_BOOL quiet)
+{
+ OSStatus ortn;
+ int ourRtn = 0;
+
+ CFIndex numRefItems = CFArrayGetCount(refArray);
+ CFIndex numTestItems = CFArrayGetCount(testArray);
+ if(numRefItems != numTestItems) {
+ printf("***item count mismatch: ref %ld test %ld\n",
+ numRefItems, numTestItems);
+ return 1;
+ }
+ for(CFIndex dex=0; dex<numRefItems; dex++) {
+ CFTypeRef refItem = CFArrayGetValueAtIndex(refArray, dex);
+ CFTypeRef testItem = CFArrayGetValueAtIndex(testArray, dex);
+ CFTypeID theType = CFGetTypeID(refItem);
+ if(theType != CFGetTypeID(testItem)) {
+ printf("***item type mismatch: ref %ld test %ld\n",
+ theType, CFGetTypeID(testItem));
+ return 1;
+ }
+ if(theType == SecCertificateGetTypeID()) {
+ /* cert: compare raw data */
+ CSSM_DATA refData;
+ CSSM_DATA testData;
+ ortn = SecCertificateGetData((SecCertificateRef)refItem, &refData);
+ if(ortn) {
+ cssmPerror("SecCertificateGetData", ortn);
+ return ++ourRtn;
+ }
+ ortn = SecCertificateGetData((SecCertificateRef)testItem, &testData);
+ if(ortn) {
+ cssmPerror("SecCertificateGetData", ortn);
+ return ++ourRtn;
+ }
+ if(!appCompareCssmData(&refData, &testData)) {
+ printf("***Data miscompare on cert %ld\n", dex);
+ ourRtn = testError(quiet);
+ if(ourRtn) {
+ return ourRtn;
+ }
+ }
+ }
+ else if(theType == SecKeyGetTypeID()) {
+ /* Keys - an inexact science to be sure since we don't attempt
+ * to access the raw key material */
+
+ const CSSM_KEY *refKey;
+ ortn = SecKeyGetCSSMKey((SecKeyRef)refItem, &refKey);
+ if(ortn) {
+ cssmPerror("SecKeyGetCSSMKey", ortn);
+ return ++ourRtn;
+ }
+ const CSSM_KEY *testKey;
+ ortn = SecKeyGetCSSMKey((SecKeyRef)testItem, &testKey);
+ if(ortn) {
+ cssmPerror("SecPkcs12GetCssmPrivateKey", ortn);
+ return ++ourRtn;
+ }
+
+ /* compare key sizes and algorithm */
+ if(refKey->KeyHeader.LogicalKeySizeInBits !=
+ testKey->KeyHeader.LogicalKeySizeInBits) {
+ printf("***Key size miscompare on Key %ld\n", dex);
+ ourRtn = testError(quiet);
+ if(ourRtn) {
+ return ourRtn;
+ }
+ }
+ if(refKey->KeyHeader.AlgorithmId !=
+ testKey->KeyHeader.AlgorithmId) {
+ printf("***AlgorithmId miscompare on Key %ld\n", dex);
+ ourRtn = testError(quiet);
+ if(ourRtn) {
+ return ourRtn;
+ }
+ }
+ }
+ else {
+ /* this program may need work here. e.g. for SecCRLRefs */
+ printf("***Unknown type ID (%ld)\n", theType);
+ ourRtn++;
+ }
+ }
+
+ return ourRtn;
+}
+
+int main(int argc, char **argv)
+{
+ unsigned char *pfx;
+ unsigned pfxLen;
+ SecKeychainRef kcRef1 = nil; // reference, 1st import destination
+ SecKeychainRef kcRef2 = nil; // subsequent import destination
+
+ CSSM_BOOL quiet = CSSM_FALSE;
+ unsigned loops = 10;
+ bool verbose = false;
+ bool doPause = false;
+ char *kcName = NULL;
+
+ int i;
+
+ if(argc < 5) {
+ usage(argv);
+ }
+
+ if(readFile(argv[1], &pfx, &pfxLen)) {
+ printf("***Error reading PFX from %s. Aborting.\n", argv[1]);
+ exit(1);
+ }
+ CFStringRef pwd = CFStringCreateWithCString(NULL, argv[2],
+ kCFStringEncodingASCII);
+ if(pwd == NULL) {
+ printf("Bad password (%s)\n", argv[2]);
+ exit(1);
+ }
+ kcName = argv[3];
+ OSStatus ortn = SecKeychainOpen(kcName, &kcRef1);
+ if(ortn) {
+ cssmPerror("SecKeychainOpen", ortn);
+ exit(1);
+ }
+ kcName = argv[4];
+ ortn = SecKeychainOpen(kcName, &kcRef2);
+ if(ortn) {
+ cssmPerror("SecKeychainOpen", ortn);
+ exit(1);
+ }
+
+ for(i=5; i<argc; i++) {
+ char *arg = argv[i];
+ switch(arg[0]) {
+ case 'l':
+ loops = atoi(&arg[2]);
+ break;
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'p':
+ doPause = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ /* do first decode to get the PFX into "our" form */
+ CFArrayRef refArray;
+ CFDataRef cfdPfx = CFDataCreate(NULL, pfx, pfxLen);
+
+ if(verbose) {
+ printf(" ...initial decode\n");
+ }
+ ortn = p12Import(cfdPfx, pwd, kcRef1, &refArray);
+ if(ortn) {
+ printf("Error on initial p12Import; aborting.\n");
+ exit(1);
+ }
+
+ /* reencode. At this point the PFXs will not be identical since
+ * everyone packages these up a little differently. */
+ CFDataRef refPfx = NULL;
+ if(verbose) {
+ printf(" ...first reencode\n");
+ }
+ ortn = p12Export(refArray, pwd, &refPfx);
+ if(ortn) {
+ printf("Error on initial p12Export; aborting.\n");
+ exit(1);
+ }
+ CFDataRef pfxToDecode = refPfx;
+ CFRetain(pfxToDecode);
+
+ for(unsigned loop=0; loop<loops; loop++) {
+ if(!quiet) {
+ printf("..loop %u\n", loop);
+ }
+ CFArrayRef testArray;
+ if(verbose) {
+ printf(" ...decode\n");
+ }
+ ortn = p12Import(pfxToDecode, pwd, kcRef2, &testArray);
+ if(ortn) {
+ return ortn;
+ }
+
+ /*
+ * Compare that decode to our original
+ */
+ if(compareDecodedArrays(refArray, testArray, quiet)) {
+ exit(1);
+ }
+
+ /* now reencode, should get blob with same length but different
+ * data (because salt is random each time) */
+ CFDataRef newPfx = NULL;
+ if(verbose) {
+ printf(" ...reencode\n");
+ }
+ ortn = p12Export(testArray, pwd, &newPfx);
+ if(ortn) {
+ exit(1);
+ }
+
+ if(CFDataGetLength(refPfx) != CFDataGetLength(newPfx)) {
+ printf("***PFX length miscompare after reencode\n");
+ writeBlobs(refPfx, newPfx);
+ return 1;
+ }
+ if(!memcmp(CFDataGetBytePtr(refPfx), CFDataGetBytePtr(newPfx),
+ CFDataGetLength(refPfx))) {
+ printf("***Unexpected PFX data compare after reencode\n");
+ writeBlobs(refPfx, newPfx);
+ return 1;
+ }
+ CFRelease(pfxToDecode);
+ pfxToDecode = newPfx;
+ if(doPause) {
+ fpurge(stdin);
+ printf("Hit CR to continue: ");
+ getchar();
+ }
+
+ /* delete everything we imported into kcRef2 */
+ CFIndex numItems = CFArrayGetCount(testArray);
+ for(CFIndex dex=0; dex<numItems; dex++) {
+ SecKeychainItemRef itemRef =
+ (SecKeychainItemRef)CFArrayGetValueAtIndex(refArray, dex);
+ ortn = SecKeychainItemDelete(itemRef);
+ if(ortn) {
+ cssmPerror("SecKeychainItemDelete", ortn);
+ /*
+ * keep going, but if we're looping this will result in a dup
+ * item error on the next import
+ */
+ }
+ }
+ CFRelease(testArray);
+ }
+ if(!quiet) {
+ printf("...p12Reencode complete\n");
+ }
+ return ortn;
+}
+