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