signerAndSubjSsl.cpp   [plain text]


/* Copyright (c) 1998-2003,2005-2006 Apple Computer, Inc.
 *
 * signerAndSubjSsl.c
 *
 * Create two certs - a root, and a subject cert signed by the root. 
 * Includes subjectAltName extension for leaf cert.
 * This version uses CSSM_TP_SubmitCredRequest to create the certs.
 *
 */

#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <clAppUtils/clutils.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Security/cssm.h>
#include <Security/x509defs.h>
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <Security/oidsalg.h>
#include <Security/certextensions.h>
#include <Security/cssmapple.h>
#include <string.h>

/* key labels */
#define SUBJ_KEY_LABEL		"subjectKey"
#define ROOT_KEY_LABEL		"rootKey"

/* default key and signature algorithm */
#define SIG_ALG_DEFAULT		CSSM_ALGID_SHA1WithRSA
#define SIG_OID_DEFAULT		CSSMOID_SHA1WithRSA
#define KEY_ALG_DEFAULT		CSSM_ALGID_RSA

/* for write certs option */
#define ROOT_CERT_FILE_NAME		"ssRootCert.der"
#define SUBJ_CERT_FILE_NAME		"ssSubjCert.der"

/* public key in ref form, TP supports this as of 1/30/02 */
#define PUB_KEY_IS_REF			CSSM_TRUE

static void usage(char **argv)
{
	printf("Usage: %s [options]\n", argv[0]);
	printf("Options:\n");
	printf("    i=IP_Address for subjectAltName\n");
	printf("    d=dnsName for subjectAltName\n");
	printf("    k=keySizeInBits\n");
	printf("    q(uiet)\n");
	exit(1);
}

/*
 * RDN components for root, subject
 */
CSSM_APPLE_TP_NAME_OID rootRdn[] = 
{
	{ "Apple Computer",					&CSSMOID_OrganizationName },
	{ "The Big Cheese",					&CSSMOID_Title }
};
#define NUM_ROOT_NAMES	(sizeof(rootRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))

CSSM_APPLE_TP_NAME_OID subjRdn[] = 
{
	{ "Apple Computer",					&CSSMOID_OrganizationName },
	{ "something.org",					&CSSMOID_CommonName }
};
#define NUM_SUBJ_NAMES	(sizeof(subjRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))

/*
 * Convert a string containing a dotted IP address to 4 bytes.
 * Returns nonzero on error.
 */
static int convertIp(
	const char 	*str,
	uint8 		*buf)
{
	char cbuf[4];
	for(unsigned dex=0; dex<3; dex++) {
		char *nextDot = strchr(str, '.');
		if(nextDot == NULL) {
			return 1;
		}
		memmove(cbuf, str, nextDot - str);
		*buf = atoi(cbuf);
		buf++;				// next out char
		str = nextDot + 1;	// next in char after dot
		
	}
	/* str points to last char */
	if(str == NULL) {
		return 1;
	}
	*buf = atoi(str);
	return 0;
}

int main(int argc, char **argv)
{
	CSSM_CL_HANDLE	clHand;			// CL handle
	CSSM_CSP_HANDLE	cspHand;		// CSP handle
	CSSM_TP_HANDLE	tpHand;			// TP handle
	CSSM_DATA		signedRootCert;	// from CSSM_CL_CertSign
	CSSM_DATA		signedSubjCert;	// from CSSM_CL_CertSign
	CSSM_KEY		subjPubKey;		// subject's RSA public key blob
	CSSM_KEY		subjPrivKey;	// subject's RSA private key - ref format
	CSSM_KEY		rootPubKey;		// root's RSA public key blob
	CSSM_KEY		rootPrivKey;	// root's RSA private key - ref format
	CSSM_RETURN		crtn;
	int				arg;
	unsigned		errorCount = 0;
	CSSM_DATA		refId;			// mallocd by CSSM_TP_SubmitCredRequest
	CSSM_APPLE_TP_CERT_REQUEST	certReq;
	CSSM_TP_REQUEST_SET			reqSet;
	sint32						estTime;
	CSSM_BOOL					confirmRequired;
	CSSM_TP_RESULT_SET_PTR		resultSet;
	CSSM_ENCODED_CERT			*encCert;
	CSSM_TP_CALLERAUTH_CONTEXT 	CallerAuthContext;
	CSSM_FIELD					policyId;
	CE_GeneralNames				genNames;
	CE_GeneralName				genName;
	uint8						ipNameBuf[4];
	
	/* user-spec'd variables */
	CSSM_ALGORITHMS	keyAlg = KEY_ALG_DEFAULT;
	CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT;
	CSSM_OID		sigOid = SIG_OID_DEFAULT;
	uint32			keySizeInBits = CSP_KEY_SIZE_DEFAULT;
	char			*ipAddrs = NULL;
	char			*dnsName = NULL;
	CSSM_BOOL		quiet = CSSM_FALSE;
	
	/* 
	 * Two extensions. Subject has two (KeyUsage and possibly 
	 * subjectAltName); root has KeyUsage and  BasicConstraints.
	 */
	CE_DataAndType 			rootExts[2];
	CE_DataAndType 			leafExts[2];
	unsigned				numLeafExts;
	
	for(arg=1; arg<argc; arg++) {
		char *argp = argv[arg];
		switch(argp[0]) {
		    case 'k':
				keySizeInBits = atoi(&argp[2]);
				break;
			case 'i':
				ipAddrs = &argp[2];
				break;
			case 'd':
				dnsName = &argp[2];
				break;
			case 'q':
				quiet = CSSM_TRUE;
				break;
			default:
				usage(argv);
		}
	}
	
	if(ipAddrs && dnsName) {
		printf("Max of one of {ipAddrs, dnsName} at a time, please.\n");
		usage(argv);
	}
	if(ipAddrs) {
		if(convertIp(ipAddrs, ipNameBuf)) {
			printf("**Malformed IP address. Aborting.\n");
			exit(1);
		}
	}
	
	/* connect to CL, TP, and CSP */
	clHand = clStartup();
	if(clHand == 0) {
		return 0;
	}
	tpHand = tpStartup();
	if(tpHand == 0) {
		return 0;
	}
	cspHand = cspStartup();
	if(cspHand == 0) {
		return 0;
	}

	/* subsequent errors to abort: to detach */
	
	/* cook up an RSA key pair for the subject */
	crtn = cspGenKeyPair(cspHand,
		keyAlg,
		SUBJ_KEY_LABEL,
		strlen(SUBJ_KEY_LABEL),
		keySizeInBits,
		&subjPubKey,
		#if PUB_KEY_IS_REF
		CSSM_TRUE,
		#else
		CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
		#endif
		CSSM_KEYUSE_VERIFY,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		&subjPrivKey,
		CSSM_TRUE,			// privIsRef - doesn't matter
		CSSM_KEYUSE_SIGN,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		CSSM_FALSE);
	if(crtn) {
		errorCount++;
		goto abort;
	}

	/* and the root */
	crtn = cspGenKeyPair(cspHand,
		keyAlg,
		ROOT_KEY_LABEL,
		strlen(ROOT_KEY_LABEL),
		keySizeInBits,
		&rootPubKey,
		CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
		CSSM_KEYUSE_VERIFY,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		&rootPrivKey,
		CSSM_TRUE,			// privIsRef - doesn't matter
		CSSM_KEYUSE_SIGN,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		CSSM_FALSE);
	if(crtn) {
		errorCount++;
		goto abort;
	}

	/* A KeyUsage extension for both certs */
	rootExts[0].type = DT_KeyUsage;
	rootExts[0].critical = CSSM_FALSE;
	rootExts[0].extension.keyUsage = 
		CE_KU_DigitalSignature | CE_KU_KeyCertSign;

	leafExts[0].type = DT_KeyUsage;
	leafExts[0].critical = CSSM_FALSE;
	leafExts[0].extension.keyUsage =  CE_KU_DigitalSignature;

	/* BasicConstraints for root only */
	rootExts[1].type = DT_BasicConstraints;
	rootExts[1].critical = CSSM_TRUE;
	rootExts[1].extension.basicConstraints.cA = CSSM_TRUE;
	rootExts[1].extension.basicConstraints.pathLenConstraintPresent = 
			CSSM_TRUE;
	rootExts[1].extension.basicConstraints.pathLenConstraint = 2;

	/* possible subjectAltName for leaf */
	numLeafExts = 1;
	if(ipAddrs || dnsName) {
		numLeafExts++;
		leafExts[1].type = DT_SubjectAltName;
		leafExts[1].critical = CSSM_TRUE;
		
		genName.berEncoded = CSSM_FALSE;
		if(ipAddrs) {
			genName.name.Data = (uint8 *)ipNameBuf;
			genName.name.Length = 4;
			genName.nameType = GNT_IPAddress;
		}
		else {
			genName.name.Data = (uint8 *)dnsName;
			genName.nameType = GNT_DNSName;
			genName.name.Length = strlen(dnsName);
		}
		genNames.numNames = 1;
		genNames.generalName = &genName;
		leafExts[1].extension.subjectAltName = genNames;
	}
	
	/* certReq for root */
	memset(&certReq, 0, sizeof(CSSM_APPLE_TP_CERT_REQUEST));
	certReq.cspHand = cspHand;
	certReq.clHand = clHand;
	certReq.serialNumber = 0x12345678;
	certReq.numSubjectNames = NUM_ROOT_NAMES;
	certReq.subjectNames = rootRdn;
	certReq.numIssuerNames = 0;
	certReq.issuerNames = NULL;
	certReq.certPublicKey = &rootPubKey;
	certReq.issuerPrivateKey = &rootPrivKey;
	certReq.signatureAlg = sigAlg;
	certReq.signatureOid = sigOid;
	certReq.notBefore = 0;			// now
	certReq.notAfter = 10000;		// seconds from now
	certReq.numExtensions = 2;
	certReq.extensions = rootExts;
	
	reqSet.NumberOfRequests = 1;
	reqSet.Requests = &certReq;
	
	/* a big CSSM_TP_CALLERAUTH_CONTEXT just to specify an OID */
	memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
	memset(&policyId, 0, sizeof(CSSM_FIELD));
	policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
	CallerAuthContext.Policy.NumberOfPolicyIds = 1;
	CallerAuthContext.Policy.PolicyIds = &policyId;
	
	/* generate root cert */
	crtn = CSSM_TP_SubmitCredRequest(tpHand,
		NULL,				// PreferredAuthority
		CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
		&reqSet,
		&CallerAuthContext,	
		&estTime,
		&refId);
	if(crtn) {
		printError("CSSM_TP_SubmitCredRequest", crtn);
		errorCount++;
		goto abort;
	}
	crtn = CSSM_TP_RetrieveCredResult(tpHand,
		&refId,
		NULL,				// CallerAuthCredentials
		&estTime,
		&confirmRequired,
		&resultSet);
	if(crtn) {
		printError("CSSM_TP_RetrieveCredResult", crtn);
		errorCount++;
		goto abort;
	}
	if(resultSet == NULL) {
		printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
		errorCount++;
		goto abort;
	}
	encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
	signedRootCert = encCert->CertBlob;

	writeFile(ROOT_CERT_FILE_NAME, signedRootCert.Data, 
		signedRootCert.Length);
	if(!quiet) {
		printf("...wrote %lu bytes to %s\n", signedRootCert.Length, 
			ROOT_CERT_FILE_NAME);
	}

	/* now a subject cert signed by the root cert */
	certReq.serialNumber = 0x8765;
	certReq.numSubjectNames = NUM_SUBJ_NAMES;
	certReq.subjectNames = subjRdn;
	certReq.numIssuerNames = NUM_ROOT_NAMES;
	certReq.issuerNames = rootRdn;
	certReq.certPublicKey = &subjPubKey;
	certReq.issuerPrivateKey = &rootPrivKey;
	certReq.numExtensions = numLeafExts;
	certReq.extensions = leafExts;

	crtn = CSSM_TP_SubmitCredRequest(tpHand,
		NULL,				// PreferredAuthority
		CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
		&reqSet,
		&CallerAuthContext,
		&estTime,
		&refId);
	if(crtn) {
		printError("CSSM_TP_SubmitCredRequest (2)", crtn);
		errorCount++;
		goto abort;
	}
	crtn = CSSM_TP_RetrieveCredResult(tpHand,
		&refId,
		NULL,				// CallerAuthCredentials
		&estTime,
		&confirmRequired,
		&resultSet);		// leaks.....
	if(crtn) {
		printError("CSSM_TP_RetrieveCredResult (2)", crtn);
		errorCount++;
		goto abort;
	}
	if(resultSet == NULL) {
		printf("***CSSM_TP_RetrieveCredResult (2) returned NULL result set.\n");
		errorCount++;
		goto abort;
	}
	encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
	signedSubjCert = encCert->CertBlob;

	writeFile(SUBJ_CERT_FILE_NAME, signedSubjCert.Data, 
		signedSubjCert.Length);
	if(!quiet) {
		printf("...wrote %lu bytes to %s\n", signedSubjCert.Length, 
			SUBJ_CERT_FILE_NAME);
	}
	
	/* free/delete certs and keys */
	appFreeCssmData(&signedSubjCert, CSSM_FALSE);
	appFreeCssmData(&signedRootCert, CSSM_FALSE);

	cspFreeKey(cspHand, &rootPubKey);
	cspFreeKey(cspHand, &subjPubKey);

abort:
	if(cspHand != 0) {
		CSSM_ModuleDetach(cspHand);
	}
	if(clHand != 0) {
		CSSM_ModuleDetach(clHand);
	}
	if(tpHand != 0) {
		CSSM_ModuleDetach(tpHand);
	}

	return 0;
}