1/* 2 * p12Reencode - take a p12 PFX, decode and reencode 3 */ 4#include <Security/SecImportExport.h> 5#include <Security/Security.h> 6#include <stdio.h> 7#include <stdlib.h> 8#include <security_cdsa_utils/cuFileIo.h> 9#include <utilLib/common.h> 10 11static void usage(char **argv) 12{ 13 printf("Usage: %s pfx password keychain1 keychain2 [l=loops] [q(uiet)] " 14 "[v(erbose)]\n", argv[0]); 15 exit(1); 16} 17 18 19#define WRITE_BLOBS 0 20#if WRITE_BLOBS 21static void writeBlobs(CFDataRef pfx1, CFDataRef pfx2) 22{ 23 writeFile("pfx1.der", CFDataGetBytePtr(pfx1), CFDataGetLength(pfx1)); 24 writeFile("pfx2.der", CFDataGetBytePtr(pfx2), CFDataGetLength(pfx2)); 25 printf("...wrote %u bytes to pfx1.der, %u bytes to pfx2.der\n", 26 CFDataGetLength(pfx1), CFDataGetLength(pfx2)); 27} 28#else 29#define writeBlobs(p1, p2) 30#endif 31 32#if 0 33/* Not possible using import/export API */ 34/* compare attrs, all of which are optional */ 35static int compareAttrs( 36 CFStringRef refFriendlyName, 37 CFDataRef refLocalKeyId, 38 CFStringRef testFriendlyName, 39 CFDataRef testLocalKeyId, 40 char *itemType, 41 CSSM_BOOL quiet) 42{ 43 if(refFriendlyName == NULL) { 44 if(testFriendlyName != NULL) { 45 printf("****s refFriendlyName NULL, testFriendlyName " 46 "non-NULL\n", itemType); 47 return testError(quiet); 48 } 49 } 50 else { 51 CFComparisonResult res = CFStringCompare(refFriendlyName, 52 testFriendlyName, 0); 53 if(res != kCFCompareEqualTo) { 54 printf("***%s friendlyName Miscompare\n", itemType); 55 return testError(quiet); 56 } 57 } 58 59 if(refLocalKeyId == NULL) { 60 if(testLocalKeyId != NULL) { 61 printf("****s refLocalKeyId NULL, testLocalKeyId " 62 "non-NULL\n", itemType); 63 return testError(quiet); 64 } 65 } 66 else { 67 if(compareCfData(refLocalKeyId, testLocalKeyId)) { 68 printf("***%s localKeyId Miscompare\n", itemType); 69 return testError(quiet); 70 } 71 } 72 73 /* release the attrs */ 74 if(refFriendlyName) { 75 CFRelease(refFriendlyName); 76 } 77 if(refLocalKeyId) { 78 CFRelease(refLocalKeyId); 79 } 80 if(testFriendlyName) { 81 CFRelease(testFriendlyName); 82 } 83 if(testLocalKeyId) { 84 CFRelease(testLocalKeyId); 85 } 86 return 0; 87} 88#endif 89 90static void setUpKeyParams( 91 SecKeyImportExportParameters &keyParams, 92 CFStringRef pwd) 93{ 94 memset(&keyParams, 0, sizeof(keyParams)); 95 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; 96 keyParams.passphrase = pwd; 97} 98 99/* 100 * Basic import/export: convert between CFArray of keychain items and a CFDataRef 101 */ 102static OSStatus p12Import( 103 CFDataRef pfx, 104 CFStringRef pwd, 105 SecKeychainRef kcRef, 106 CFArrayRef *outArray) 107{ 108 SecKeyImportExportParameters keyParams; 109 setUpKeyParams(keyParams, pwd); 110 OSStatus ortn; 111 SecExternalFormat format = kSecFormatPKCS12; 112 113 ortn = SecKeychainItemImport(pfx, NULL, &format, NULL, 0, &keyParams, 114 kcRef, outArray); 115 if(ortn) { 116 cssmPerror("SecKeychainItemImport", ortn); 117 } 118 return ortn; 119} 120 121static OSStatus p12Export( 122 CFArrayRef inArray, 123 CFStringRef pwd, 124 CFDataRef *pfx) 125{ 126 SecKeyImportExportParameters keyParams; 127 setUpKeyParams(keyParams, pwd); 128 OSStatus ortn; 129 130 ortn = SecKeychainItemExport(inArray, kSecFormatPKCS12, 0, &keyParams, pfx); 131 if(ortn) { 132 cssmPerror("SecKeychainItemExport", ortn); 133 } 134 return ortn; 135} 136 137/* 138 * Compare two CFArrayRefs containing various items, subsequent to decode. Returns 139 * nonzero if they differ. 140 * 141 * As of April 9 2004, we do NOT see CRLs so we don't compare them. I think 142 * we need a SecCRLRef... 143 */ 144static int compareDecodedArrays( 145 CFArrayRef refArray, 146 CFArrayRef testArray, 147 CSSM_BOOL quiet) 148{ 149 OSStatus ortn; 150 int ourRtn = 0; 151 152 CFIndex numRefItems = CFArrayGetCount(refArray); 153 CFIndex numTestItems = CFArrayGetCount(testArray); 154 if(numRefItems != numTestItems) { 155 printf("***item count mismatch: ref %ld test %ld\n", 156 numRefItems, numTestItems); 157 return 1; 158 } 159 for(CFIndex dex=0; dex<numRefItems; dex++) { 160 CFTypeRef refItem = CFArrayGetValueAtIndex(refArray, dex); 161 CFTypeRef testItem = CFArrayGetValueAtIndex(testArray, dex); 162 CFTypeID theType = CFGetTypeID(refItem); 163 if(theType != CFGetTypeID(testItem)) { 164 printf("***item type mismatch: ref %ld test %ld\n", 165 theType, CFGetTypeID(testItem)); 166 return 1; 167 } 168 if(theType == SecCertificateGetTypeID()) { 169 /* cert: compare raw data */ 170 CSSM_DATA refData; 171 CSSM_DATA testData; 172 ortn = SecCertificateGetData((SecCertificateRef)refItem, &refData); 173 if(ortn) { 174 cssmPerror("SecCertificateGetData", ortn); 175 return ++ourRtn; 176 } 177 ortn = SecCertificateGetData((SecCertificateRef)testItem, &testData); 178 if(ortn) { 179 cssmPerror("SecCertificateGetData", ortn); 180 return ++ourRtn; 181 } 182 if(!appCompareCssmData(&refData, &testData)) { 183 printf("***Data miscompare on cert %ld\n", dex); 184 ourRtn = testError(quiet); 185 if(ourRtn) { 186 return ourRtn; 187 } 188 } 189 } 190 else if(theType == SecKeyGetTypeID()) { 191 /* Keys - an inexact science to be sure since we don't attempt 192 * to access the raw key material */ 193 194 const CSSM_KEY *refKey; 195 ortn = SecKeyGetCSSMKey((SecKeyRef)refItem, &refKey); 196 if(ortn) { 197 cssmPerror("SecKeyGetCSSMKey", ortn); 198 return ++ourRtn; 199 } 200 const CSSM_KEY *testKey; 201 ortn = SecKeyGetCSSMKey((SecKeyRef)testItem, &testKey); 202 if(ortn) { 203 cssmPerror("SecPkcs12GetCssmPrivateKey", ortn); 204 return ++ourRtn; 205 } 206 207 /* compare key sizes and algorithm */ 208 if(refKey->KeyHeader.LogicalKeySizeInBits != 209 testKey->KeyHeader.LogicalKeySizeInBits) { 210 printf("***Key size miscompare on Key %ld\n", dex); 211 ourRtn = testError(quiet); 212 if(ourRtn) { 213 return ourRtn; 214 } 215 } 216 if(refKey->KeyHeader.AlgorithmId != 217 testKey->KeyHeader.AlgorithmId) { 218 printf("***AlgorithmId miscompare on Key %ld\n", dex); 219 ourRtn = testError(quiet); 220 if(ourRtn) { 221 return ourRtn; 222 } 223 } 224 } 225 else { 226 /* this program may need work here. e.g. for SecCRLRefs */ 227 printf("***Unknown type ID (%ld)\n", theType); 228 ourRtn++; 229 } 230 } 231 232 return ourRtn; 233} 234 235int main(int argc, char **argv) 236{ 237 unsigned char *pfx; 238 unsigned pfxLen; 239 SecKeychainRef kcRef1 = nil; // reference, 1st import destination 240 SecKeychainRef kcRef2 = nil; // subsequent import destination 241 242 CSSM_BOOL quiet = CSSM_FALSE; 243 unsigned loops = 10; 244 bool verbose = false; 245 bool doPause = false; 246 char *kcName = NULL; 247 248 int i; 249 250 if(argc < 5) { 251 usage(argv); 252 } 253 254 if(readFile(argv[1], &pfx, &pfxLen)) { 255 printf("***Error reading PFX from %s. Aborting.\n", argv[1]); 256 exit(1); 257 } 258 CFStringRef pwd = CFStringCreateWithCString(NULL, argv[2], 259 kCFStringEncodingASCII); 260 if(pwd == NULL) { 261 printf("Bad password (%s)\n", argv[2]); 262 exit(1); 263 } 264 kcName = argv[3]; 265 OSStatus ortn = SecKeychainOpen(kcName, &kcRef1); 266 if(ortn) { 267 cssmPerror("SecKeychainOpen", ortn); 268 exit(1); 269 } 270 kcName = argv[4]; 271 ortn = SecKeychainOpen(kcName, &kcRef2); 272 if(ortn) { 273 cssmPerror("SecKeychainOpen", ortn); 274 exit(1); 275 } 276 277 for(i=5; i<argc; i++) { 278 char *arg = argv[i]; 279 switch(arg[0]) { 280 case 'l': 281 loops = atoi(&arg[2]); 282 break; 283 case 'q': 284 quiet = CSSM_TRUE; 285 break; 286 case 'p': 287 doPause = true; 288 break; 289 case 'v': 290 verbose = true; 291 break; 292 default: 293 usage(argv); 294 } 295 } 296 297 /* do first decode to get the PFX into "our" form */ 298 CFArrayRef refArray; 299 CFDataRef cfdPfx = CFDataCreate(NULL, pfx, pfxLen); 300 301 if(verbose) { 302 printf(" ...initial decode\n"); 303 } 304 ortn = p12Import(cfdPfx, pwd, kcRef1, &refArray); 305 if(ortn) { 306 printf("Error on initial p12Import; aborting.\n"); 307 exit(1); 308 } 309 310 /* reencode. At this point the PFXs will not be identical since 311 * everyone packages these up a little differently. */ 312 CFDataRef refPfx = NULL; 313 if(verbose) { 314 printf(" ...first reencode\n"); 315 } 316 ortn = p12Export(refArray, pwd, &refPfx); 317 if(ortn) { 318 printf("Error on initial p12Export; aborting.\n"); 319 exit(1); 320 } 321 CFDataRef pfxToDecode = refPfx; 322 CFRetain(pfxToDecode); 323 324 for(unsigned loop=0; loop<loops; loop++) { 325 if(!quiet) { 326 printf("..loop %u\n", loop); 327 } 328 CFArrayRef testArray; 329 if(verbose) { 330 printf(" ...decode\n"); 331 } 332 ortn = p12Import(pfxToDecode, pwd, kcRef2, &testArray); 333 if(ortn) { 334 return ortn; 335 } 336 337 /* 338 * Compare that decode to our original 339 */ 340 if(compareDecodedArrays(refArray, testArray, quiet)) { 341 exit(1); 342 } 343 344 /* now reencode, should get blob with same length but different 345 * data (because salt is random each time) */ 346 CFDataRef newPfx = NULL; 347 if(verbose) { 348 printf(" ...reencode\n"); 349 } 350 ortn = p12Export(testArray, pwd, &newPfx); 351 if(ortn) { 352 exit(1); 353 } 354 355 if(CFDataGetLength(refPfx) != CFDataGetLength(newPfx)) { 356 printf("***PFX length miscompare after reencode\n"); 357 writeBlobs(refPfx, newPfx); 358 return 1; 359 } 360 if(!memcmp(CFDataGetBytePtr(refPfx), CFDataGetBytePtr(newPfx), 361 CFDataGetLength(refPfx))) { 362 printf("***Unexpected PFX data compare after reencode\n"); 363 writeBlobs(refPfx, newPfx); 364 return 1; 365 } 366 CFRelease(pfxToDecode); 367 pfxToDecode = newPfx; 368 if(doPause) { 369 fpurge(stdin); 370 printf("Hit CR to continue: "); 371 getchar(); 372 } 373 374 /* delete everything we imported into kcRef2 */ 375 CFIndex numItems = CFArrayGetCount(testArray); 376 for(CFIndex dex=0; dex<numItems; dex++) { 377 SecKeychainItemRef itemRef = 378 (SecKeychainItemRef)CFArrayGetValueAtIndex(refArray, dex); 379 ortn = SecKeychainItemDelete(itemRef); 380 if(ortn) { 381 cssmPerror("SecKeychainItemDelete", ortn); 382 /* 383 * keep going, but if we're looping this will result in a dup 384 * item error on the next import 385 */ 386 } 387 } 388 CFRelease(testArray); 389 } 390 if(!quiet) { 391 printf("...p12Reencode complete\n"); 392 } 393 return ortn; 394} 395 396