1/* Copyright (c) 1998-2003,2005-2006 Apple Computer, Inc.
2 *
3 * signerAndSubjSsl.c
4 *
5 * Create two certs - a root, and a subject cert signed by the root.
6 * Includes subjectAltName extension for leaf cert.
7 * This version uses CSSM_TP_SubmitCredRequest to create the certs.
8 *
9 */
10
11#include <utilLib/common.h>
12#include <utilLib/cspwrap.h>
13#include <security_cdsa_utils/cuFileIo.h>
14#include <clAppUtils/clutils.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18#include <Security/cssm.h>
19#include <Security/x509defs.h>
20#include <Security/oidsattr.h>
21#include <Security/oidscert.h>
22#include <Security/oidsalg.h>
23#include <Security/certextensions.h>
24#include <Security/cssmapple.h>
25#include <string.h>
26
27/* key labels */
28#define SUBJ_KEY_LABEL		"subjectKey"
29#define ROOT_KEY_LABEL		"rootKey"
30
31/* default key and signature algorithm */
32#define SIG_ALG_DEFAULT		CSSM_ALGID_SHA1WithRSA
33#define SIG_OID_DEFAULT		CSSMOID_SHA1WithRSA
34#define KEY_ALG_DEFAULT		CSSM_ALGID_RSA
35
36/* for write certs option */
37#define ROOT_CERT_FILE_NAME		"ssRootCert.der"
38#define SUBJ_CERT_FILE_NAME		"ssSubjCert.der"
39
40/* public key in ref form, TP supports this as of 1/30/02 */
41#define PUB_KEY_IS_REF			CSSM_TRUE
42
43static void usage(char **argv)
44{
45	printf("Usage: %s [options]\n", argv[0]);
46	printf("Options:\n");
47	printf("    i=IP_Address for subjectAltName\n");
48	printf("    d=dnsName for subjectAltName\n");
49	printf("    k=keySizeInBits\n");
50	printf("    q(uiet)\n");
51	exit(1);
52}
53
54/*
55 * RDN components for root, subject
56 */
57CSSM_APPLE_TP_NAME_OID rootRdn[] =
58{
59	{ "Apple Computer",					&CSSMOID_OrganizationName },
60	{ "The Big Cheese",					&CSSMOID_Title }
61};
62#define NUM_ROOT_NAMES	(sizeof(rootRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))
63
64CSSM_APPLE_TP_NAME_OID subjRdn[] =
65{
66	{ "Apple Computer",					&CSSMOID_OrganizationName },
67	{ "something.org",					&CSSMOID_CommonName }
68};
69#define NUM_SUBJ_NAMES	(sizeof(subjRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))
70
71/*
72 * Convert a string containing a dotted IP address to 4 bytes.
73 * Returns nonzero on error.
74 */
75static int convertIp(
76	const char 	*str,
77	uint8 		*buf)
78{
79	char cbuf[4];
80	for(unsigned dex=0; dex<3; dex++) {
81		char *nextDot = strchr(str, '.');
82		if(nextDot == NULL) {
83			return 1;
84		}
85		memmove(cbuf, str, nextDot - str);
86		*buf = atoi(cbuf);
87		buf++;				// next out char
88		str = nextDot + 1;	// next in char after dot
89
90	}
91	/* str points to last char */
92	if(str == NULL) {
93		return 1;
94	}
95	*buf = atoi(str);
96	return 0;
97}
98
99int main(int argc, char **argv)
100{
101	CSSM_CL_HANDLE	clHand;			// CL handle
102	CSSM_CSP_HANDLE	cspHand;		// CSP handle
103	CSSM_TP_HANDLE	tpHand;			// TP handle
104	CSSM_DATA		signedRootCert;	// from CSSM_CL_CertSign
105	CSSM_DATA		signedSubjCert;	// from CSSM_CL_CertSign
106	CSSM_KEY		subjPubKey;		// subject's RSA public key blob
107	CSSM_KEY		subjPrivKey;	// subject's RSA private key - ref format
108	CSSM_KEY		rootPubKey;		// root's RSA public key blob
109	CSSM_KEY		rootPrivKey;	// root's RSA private key - ref format
110	CSSM_RETURN		crtn;
111	int				arg;
112	unsigned		errorCount = 0;
113	CSSM_DATA		refId;			// mallocd by CSSM_TP_SubmitCredRequest
114	CSSM_APPLE_TP_CERT_REQUEST	certReq;
115	CSSM_TP_REQUEST_SET			reqSet;
116	sint32						estTime;
117	CSSM_BOOL					confirmRequired;
118	CSSM_TP_RESULT_SET_PTR		resultSet;
119	CSSM_ENCODED_CERT			*encCert;
120	CSSM_TP_CALLERAUTH_CONTEXT 	CallerAuthContext;
121	CSSM_FIELD					policyId;
122	CE_GeneralNames				genNames;
123	CE_GeneralName				genName;
124	uint8						ipNameBuf[4];
125
126	/* user-spec'd variables */
127	CSSM_ALGORITHMS	keyAlg = KEY_ALG_DEFAULT;
128	CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT;
129	CSSM_OID		sigOid = SIG_OID_DEFAULT;
130	uint32			keySizeInBits = CSP_KEY_SIZE_DEFAULT;
131	char			*ipAddrs = NULL;
132	char			*dnsName = NULL;
133	CSSM_BOOL		quiet = CSSM_FALSE;
134
135	/*
136	 * Two extensions. Subject has two (KeyUsage and possibly
137	 * subjectAltName); root has KeyUsage and  BasicConstraints.
138	 */
139	CE_DataAndType 			rootExts[2];
140	CE_DataAndType 			leafExts[2];
141	unsigned				numLeafExts;
142
143	for(arg=1; arg<argc; arg++) {
144		char *argp = argv[arg];
145		switch(argp[0]) {
146		    case 'k':
147				keySizeInBits = atoi(&argp[2]);
148				break;
149			case 'i':
150				ipAddrs = &argp[2];
151				break;
152			case 'd':
153				dnsName = &argp[2];
154				break;
155			case 'q':
156				quiet = CSSM_TRUE;
157				break;
158			default:
159				usage(argv);
160		}
161	}
162
163	if(ipAddrs && dnsName) {
164		printf("Max of one of {ipAddrs, dnsName} at a time, please.\n");
165		usage(argv);
166	}
167	if(ipAddrs) {
168		if(convertIp(ipAddrs, ipNameBuf)) {
169			printf("**Malformed IP address. Aborting.\n");
170			exit(1);
171		}
172	}
173
174	/* connect to CL, TP, and CSP */
175	clHand = clStartup();
176	if(clHand == 0) {
177		return 0;
178	}
179	tpHand = tpStartup();
180	if(tpHand == 0) {
181		return 0;
182	}
183	cspHand = cspStartup();
184	if(cspHand == 0) {
185		return 0;
186	}
187
188	/* subsequent errors to abort: to detach */
189
190	/* cook up an RSA key pair for the subject */
191	crtn = cspGenKeyPair(cspHand,
192		keyAlg,
193		SUBJ_KEY_LABEL,
194		strlen(SUBJ_KEY_LABEL),
195		keySizeInBits,
196		&subjPubKey,
197		#if PUB_KEY_IS_REF
198		CSSM_TRUE,
199		#else
200		CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
201		#endif
202		CSSM_KEYUSE_VERIFY,
203		CSSM_KEYBLOB_RAW_FORMAT_NONE,
204		&subjPrivKey,
205		CSSM_TRUE,			// privIsRef - doesn't matter
206		CSSM_KEYUSE_SIGN,
207		CSSM_KEYBLOB_RAW_FORMAT_NONE,
208		CSSM_FALSE);
209	if(crtn) {
210		errorCount++;
211		goto abort;
212	}
213
214	/* and the root */
215	crtn = cspGenKeyPair(cspHand,
216		keyAlg,
217		ROOT_KEY_LABEL,
218		strlen(ROOT_KEY_LABEL),
219		keySizeInBits,
220		&rootPubKey,
221		CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
222		CSSM_KEYUSE_VERIFY,
223		CSSM_KEYBLOB_RAW_FORMAT_NONE,
224		&rootPrivKey,
225		CSSM_TRUE,			// privIsRef - doesn't matter
226		CSSM_KEYUSE_SIGN,
227		CSSM_KEYBLOB_RAW_FORMAT_NONE,
228		CSSM_FALSE);
229	if(crtn) {
230		errorCount++;
231		goto abort;
232	}
233
234	/* A KeyUsage extension for both certs */
235	rootExts[0].type = DT_KeyUsage;
236	rootExts[0].critical = CSSM_FALSE;
237	rootExts[0].extension.keyUsage =
238		CE_KU_DigitalSignature | CE_KU_KeyCertSign;
239
240	leafExts[0].type = DT_KeyUsage;
241	leafExts[0].critical = CSSM_FALSE;
242	leafExts[0].extension.keyUsage =  CE_KU_DigitalSignature;
243
244	/* BasicConstraints for root only */
245	rootExts[1].type = DT_BasicConstraints;
246	rootExts[1].critical = CSSM_TRUE;
247	rootExts[1].extension.basicConstraints.cA = CSSM_TRUE;
248	rootExts[1].extension.basicConstraints.pathLenConstraintPresent =
249			CSSM_TRUE;
250	rootExts[1].extension.basicConstraints.pathLenConstraint = 2;
251
252	/* possible subjectAltName for leaf */
253	numLeafExts = 1;
254	if(ipAddrs || dnsName) {
255		numLeafExts++;
256		leafExts[1].type = DT_SubjectAltName;
257		leafExts[1].critical = CSSM_TRUE;
258
259		genName.berEncoded = CSSM_FALSE;
260		if(ipAddrs) {
261			genName.name.Data = (uint8 *)ipNameBuf;
262			genName.name.Length = 4;
263			genName.nameType = GNT_IPAddress;
264		}
265		else {
266			genName.name.Data = (uint8 *)dnsName;
267			genName.nameType = GNT_DNSName;
268			genName.name.Length = strlen(dnsName);
269		}
270		genNames.numNames = 1;
271		genNames.generalName = &genName;
272		leafExts[1].extension.subjectAltName = genNames;
273	}
274
275	/* certReq for root */
276	memset(&certReq, 0, sizeof(CSSM_APPLE_TP_CERT_REQUEST));
277	certReq.cspHand = cspHand;
278	certReq.clHand = clHand;
279	certReq.serialNumber = 0x12345678;
280	certReq.numSubjectNames = NUM_ROOT_NAMES;
281	certReq.subjectNames = rootRdn;
282	certReq.numIssuerNames = 0;
283	certReq.issuerNames = NULL;
284	certReq.certPublicKey = &rootPubKey;
285	certReq.issuerPrivateKey = &rootPrivKey;
286	certReq.signatureAlg = sigAlg;
287	certReq.signatureOid = sigOid;
288	certReq.notBefore = 0;			// now
289	certReq.notAfter = 10000;		// seconds from now
290	certReq.numExtensions = 2;
291	certReq.extensions = rootExts;
292
293	reqSet.NumberOfRequests = 1;
294	reqSet.Requests = &certReq;
295
296	/* a big CSSM_TP_CALLERAUTH_CONTEXT just to specify an OID */
297	memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
298	memset(&policyId, 0, sizeof(CSSM_FIELD));
299	policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
300	CallerAuthContext.Policy.NumberOfPolicyIds = 1;
301	CallerAuthContext.Policy.PolicyIds = &policyId;
302
303	/* generate root cert */
304	crtn = CSSM_TP_SubmitCredRequest(tpHand,
305		NULL,				// PreferredAuthority
306		CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
307		&reqSet,
308		&CallerAuthContext,
309		&estTime,
310		&refId);
311	if(crtn) {
312		printError("CSSM_TP_SubmitCredRequest", crtn);
313		errorCount++;
314		goto abort;
315	}
316	crtn = CSSM_TP_RetrieveCredResult(tpHand,
317		&refId,
318		NULL,				// CallerAuthCredentials
319		&estTime,
320		&confirmRequired,
321		&resultSet);
322	if(crtn) {
323		printError("CSSM_TP_RetrieveCredResult", crtn);
324		errorCount++;
325		goto abort;
326	}
327	if(resultSet == NULL) {
328		printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
329		errorCount++;
330		goto abort;
331	}
332	encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
333	signedRootCert = encCert->CertBlob;
334
335	writeFile(ROOT_CERT_FILE_NAME, signedRootCert.Data,
336		signedRootCert.Length);
337	if(!quiet) {
338		printf("...wrote %lu bytes to %s\n", signedRootCert.Length,
339			ROOT_CERT_FILE_NAME);
340	}
341
342	/* now a subject cert signed by the root cert */
343	certReq.serialNumber = 0x8765;
344	certReq.numSubjectNames = NUM_SUBJ_NAMES;
345	certReq.subjectNames = subjRdn;
346	certReq.numIssuerNames = NUM_ROOT_NAMES;
347	certReq.issuerNames = rootRdn;
348	certReq.certPublicKey = &subjPubKey;
349	certReq.issuerPrivateKey = &rootPrivKey;
350	certReq.numExtensions = numLeafExts;
351	certReq.extensions = leafExts;
352
353	crtn = CSSM_TP_SubmitCredRequest(tpHand,
354		NULL,				// PreferredAuthority
355		CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
356		&reqSet,
357		&CallerAuthContext,
358		&estTime,
359		&refId);
360	if(crtn) {
361		printError("CSSM_TP_SubmitCredRequest (2)", crtn);
362		errorCount++;
363		goto abort;
364	}
365	crtn = CSSM_TP_RetrieveCredResult(tpHand,
366		&refId,
367		NULL,				// CallerAuthCredentials
368		&estTime,
369		&confirmRequired,
370		&resultSet);		// leaks.....
371	if(crtn) {
372		printError("CSSM_TP_RetrieveCredResult (2)", crtn);
373		errorCount++;
374		goto abort;
375	}
376	if(resultSet == NULL) {
377		printf("***CSSM_TP_RetrieveCredResult (2) returned NULL result set.\n");
378		errorCount++;
379		goto abort;
380	}
381	encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
382	signedSubjCert = encCert->CertBlob;
383
384	writeFile(SUBJ_CERT_FILE_NAME, signedSubjCert.Data,
385		signedSubjCert.Length);
386	if(!quiet) {
387		printf("...wrote %lu bytes to %s\n", signedSubjCert.Length,
388			SUBJ_CERT_FILE_NAME);
389	}
390
391	/* free/delete certs and keys */
392	appFreeCssmData(&signedSubjCert, CSSM_FALSE);
393	appFreeCssmData(&signedRootCert, CSSM_FALSE);
394
395	cspFreeKey(cspHand, &rootPubKey);
396	cspFreeKey(cspHand, &subjPubKey);
397
398abort:
399	if(cspHand != 0) {
400		CSSM_ModuleDetach(cspHand);
401	}
402	if(clHand != 0) {
403		CSSM_ModuleDetach(clHand);
404	}
405	if(tpHand != 0) {
406		CSSM_ModuleDetach(tpHand);
407	}
408
409	return 0;
410}
411
412
413