CertBuilderApp.cpp   [plain text]


/*
 * CertBuilderApp.cpp - support for constructing certs, CDSA version
 */
 
#include "clutils.h"
#include <utilLib/common.h>
#include "CertBuilderApp.h"
#include "timeStr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Security/oidscert.h>
#include <Security/oidsalg.h>
#include <Security/x509defs.h>
#include <Security/SecAsn1Coder.h>
/* private header */
#include <Security/keyTemplates.h>

/*
 * Build up a CSSM_X509_NAME from an arbitrary list of name/OID pairs. 
 * We do one a/v pair per RDN. 
 */
CSSM_X509_NAME *CB_BuildX509Name(
	const CB_NameOid *nameArray,
	unsigned numNames)
{
	CSSM_X509_NAME *top = (CSSM_X509_NAME *)appMalloc(sizeof(CSSM_X509_NAME), 0);
	if(top == NULL) {
		return NULL;
	}
	top->numberOfRDNs = numNames;
	top->RelativeDistinguishedName = 
		(CSSM_X509_RDN_PTR)appMalloc(sizeof(CSSM_X509_RDN) * numNames, 0);
	if(top->RelativeDistinguishedName == NULL) {
		return NULL;
	}
	CSSM_X509_RDN_PTR rdn;
	const CB_NameOid *nameOid;
	unsigned nameDex;
	for(nameDex=0; nameDex<numNames; nameDex++) {
		rdn = &top->RelativeDistinguishedName[nameDex];
		nameOid = &nameArray[nameDex];
		rdn->numberOfPairs = 1;
		rdn->AttributeTypeAndValue = (CSSM_X509_TYPE_VALUE_PAIR_PTR)
			appMalloc(sizeof(CSSM_X509_TYPE_VALUE_PAIR), 0);
		CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = rdn->AttributeTypeAndValue;
		if(atvp == NULL) {
			return NULL;
		}
		appCopyCssmData(nameOid->oid, &atvp->type);
		atvp->valueType = BER_TAG_PRINTABLE_STRING;
		atvp->value.Length = strlen(nameOid->string);
		atvp->value.Data = (uint8 *)CSSM_MALLOC(atvp->value.Length);
		memmove(atvp->value.Data, nameOid->string, atvp->value.Length);
	}
	return top;
}

/* free the CSSM_X509_NAME obtained from CB_BuildX509Name */
void CB_FreeX509Name(
	CSSM_X509_NAME *top)
{
	if(top == NULL) {
		return;
	}
	unsigned nameDex;
	CSSM_X509_RDN_PTR rdn;
	for(nameDex=0; nameDex<top->numberOfRDNs; nameDex++) {
		rdn = &top->RelativeDistinguishedName[nameDex];
		if(rdn->AttributeTypeAndValue) {
			for(unsigned aDex=0; aDex<rdn->numberOfPairs; aDex++) {
				CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = 
					&rdn->AttributeTypeAndValue[aDex];
				CSSM_FREE(atvp->type.Data);
				CSSM_FREE(atvp->value.Data);
			}
			CSSM_FREE(rdn->AttributeTypeAndValue);
		}
	}
	CSSM_FREE(top->RelativeDistinguishedName);
	CSSM_FREE(top);
}

/* Obtain a CSSM_X509_TIME representing "now" plus specified seconds, or
 * from a preformatted gen time string */
CSSM_X509_TIME *CB_BuildX509Time(
	unsigned secondsFromNow,	/* ignored if timeStr non-NULL */
	const char *timeStr)		/* optional, from genTimeAtNowPlus */
{
	CSSM_X509_TIME *xtime = (CSSM_X509_TIME *)appMalloc(sizeof(CSSM_X509_TIME), 0);
	if(xtime == NULL) {
		return NULL;
	}
	xtime->timeType = BER_TAG_GENERALIZED_TIME;
	char *ts;
	if(timeStr == NULL) {
		ts = genTimeAtNowPlus(secondsFromNow);
	}
	else {
		ts = (char *)appMalloc(strlen(timeStr) + 1, 0);
		strcpy(ts, timeStr);
	}
	xtime->time.Data = (uint8 *)ts;
	xtime->time.Length = strlen(ts);
	return xtime;
}

/* Free CSSM_X509_TIME obtained in CB_BuildX509Time */
void CB_FreeX509Time(
	CSSM_X509_TIME	*xtime)
{
	if(xtime == NULL) {
		return;
	}
	freeTimeString((char *)xtime->time.Data);
	appFree(xtime, 0);
}

/* 
 * Encode an OID as a CSSM_X509_ALGORITHM_IDENTIFIER.
 * Returns nonzero on error.
 * Returned data is appMallocd's caller must appFree.
 */
int encodeParamOid(
	const CSSM_OID *paramOid, 
	CSSM_DATA *params)
{
	SecAsn1CoderRef coder = NULL;
	if(SecAsn1CoderCreate(&coder)) {
		printf("***Error in SecAsn1CoderCreate()\n");
		return -1;
	}
	
	CSSM_X509_ALGORITHM_IDENTIFIER algParams;
	memset(&algParams, 0, sizeof(algParams));
	algParams.algorithm = *paramOid;
	CSSM_DATA encoded = {0, NULL};
	int ourRtn = 0;
	if(SecAsn1EncodeItem(coder, &algParams, kSecAsn1AlgorithmIDTemplate,
			&encoded)) {
		printf("***Error encoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
		ourRtn = -1;
		goto errOut;
	}
	
	/* That data is in the coder's memory space: copy ou9t to caller */
	if(appCopyCssmData(&encoded, params)) {
		printf("***encodeParamOid malloc failure\n");
		ourRtn = -1;
	}
errOut:
	SecAsn1CoderRelease(coder);
	return ourRtn;
}

/*
 * Cook up an unsigned cert.
 * This is just a wrapper for CSSM_CL_CertCreateTemplate().
 */
 
#define ALWAYS_SET_VERSION		0

CSSM_DATA_PTR CB_MakeCertTemplate(
	/* required */
	CSSM_CL_HANDLE			clHand,
	uint32					serialNumber,
	const CSSM_X509_NAME	*issuerName,	
	const CSSM_X509_NAME	*subjectName,
	const CSSM_X509_TIME	*notBefore,	
	const CSSM_X509_TIME	*notAfter,	
	const CSSM_KEY_PTR		subjectPubKey,
	CSSM_ALGORITHMS			sigAlg,			// e.g., CSSM_ALGID_SHA1WithRSA
	/* optional */
	const CSSM_DATA			*subjectUniqueId,
	const CSSM_DATA			*issuerUniqueId,
	CSSM_X509_EXTENSION		*extensions,
	unsigned				numExtensions)
{
	CSSM_FIELD		*certTemp;		
	unsigned		fieldDex = 0;		// index into certTemp
	CSSM_DATA_PTR	serialDER = NULL;	// serial number, DER format
	CSSM_DATA_PTR	rawCert;			// from CSSM_CL_CertCreateTemplate
	unsigned		version = 0;
	CSSM_DATA_PTR	versionDER = NULL;
	unsigned		extNum;
	int 			setVersion = ALWAYS_SET_VERSION;
	const CSSM_OID	*paramOid = NULL;
	
	/* convert uint32-style algorithm to the associated struct */
	CSSM_X509_ALGORITHM_IDENTIFIER algId;
	switch(sigAlg) {
		case CSSM_ALGID_SHA1WithRSA:
			algId.algorithm = CSSMOID_SHA1WithRSA;
			break;
		case CSSM_ALGID_MD5WithRSA:
			algId.algorithm = CSSMOID_MD5WithRSA;
			break;
		case CSSM_ALGID_MD2WithRSA:
			algId.algorithm = CSSMOID_MD2WithRSA;
			break;
		case CSSM_ALGID_FEE_MD5:
			algId.algorithm = CSSMOID_APPLE_FEE_MD5;
			break;
		case CSSM_ALGID_FEE_SHA1:
			algId.algorithm = CSSMOID_APPLE_FEE_SHA1;
			break;
		case CSSM_ALGID_SHA1WithECDSA:
			algId.algorithm = CSSMOID_ECDSA_WithSHA1;
			break;
		case CSSM_ALGID_SHA1WithDSA:
			algId.algorithm = CSSMOID_SHA1WithDSA_CMS;
			break;
		case CSSM_ALGID_SHA224WithRSA:
			algId.algorithm = CSSMOID_SHA224WithRSA;
			break;
		case CSSM_ALGID_SHA256WithRSA:
			algId.algorithm = CSSMOID_SHA256WithRSA;
			break;
		case CSSM_ALGID_SHA384WithRSA:
			algId.algorithm = CSSMOID_SHA384WithRSA;
			break;
		case CSSM_ALGID_SHA512WithRSA:
			algId.algorithm = CSSMOID_SHA512WithRSA;
			break;
		/* These specify the digest algorithm via an additional parameter OID */
		case CSSM_ALGID_SHA224WithECDSA:
			algId.algorithm = CSSMOID_ECDSA_WithSpecified;
			paramOid = &CSSMOID_SHA224;
			break;
		case CSSM_ALGID_SHA256WithECDSA:
			algId.algorithm = CSSMOID_ECDSA_WithSpecified;
			paramOid = &CSSMOID_SHA256;
			break;
		case CSSM_ALGID_SHA384WithECDSA:
			algId.algorithm = CSSMOID_ECDSA_WithSpecified;
			paramOid = &CSSMOID_SHA384;
			break;
		case CSSM_ALGID_SHA512WithECDSA:
			algId.algorithm = CSSMOID_ECDSA_WithSpecified;
			paramOid = &CSSMOID_SHA512;
			break;
		default:
			printf("CB_MakeCertTemplate: unknown sig alg (%u)\n", (unsigned)sigAlg);
			return NULL;
	}
	if(paramOid != NULL) {
		/* not-quite-trivial encoding of digest algorithm */
		if(encodeParamOid(paramOid, &algId.parameters)) {
			return NULL;
		}
	}
	else {
		algId.parameters.Data = NULL;
		algId.parameters.Length = 0;
	}
	
	/*
	 * version, we infer
	 * serialNumber thru subjectPubKey
	 */
	unsigned numFields = 7 + numExtensions;
	if(numExtensions) {
		version = 2;
	}
	if(subjectUniqueId) {
		numFields++;
		if(version == 0) {
			version = 1;
		}
	}
	if(issuerUniqueId) {
		numFields++;
		if(version == 0) {
			version = 1;
		}
	}
	if(version > 0) {
		setVersion = 1;
	}
	if(setVersion) {
		numFields++;
	}

	certTemp = (CSSM_FIELD *)CSSM_MALLOC(sizeof(CSSM_FIELD) * numFields);

	/* version */
	if(setVersion) {
		versionDER = intToDER(version);
		certTemp[fieldDex].FieldOid = CSSMOID_X509V1Version;
		certTemp[fieldDex++].FieldValue = *versionDER;
	}
	
	/* serial number */
	serialDER = intToDER(serialNumber);
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SerialNumber;
	certTemp[fieldDex++].FieldValue = *serialDER;

	/* subject and issuer name  */
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)issuerName;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);
	
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)subjectName;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);

	/* not before/after */
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotBefore;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)notBefore;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);

	certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotAfter;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)notAfter;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);

	/* the subject key */
	certTemp[fieldDex].FieldOid = CSSMOID_CSSMKeyStruct;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)subjectPubKey;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_KEY);

	/* signature algorithm */
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)&algId;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
	
	/* subject/issuer unique IDs */
	if(subjectUniqueId != 0) {
		certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateSubjectUniqueId;
		certTemp[fieldDex++].FieldValue = *subjectUniqueId;
	}
	if(issuerUniqueId != 0) {
		certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateIssuerUniqueId;
		certTemp[fieldDex++].FieldValue = *issuerUniqueId;
	}

	for(extNum=0; extNum<numExtensions; extNum++) {
		CSSM_X509_EXTENSION_PTR ext = &extensions[extNum];
		if(ext->format == CSSM_X509_DATAFORMAT_PARSED) {
			certTemp[fieldDex].FieldOid = ext->extnId;
		}
		else {
			certTemp[fieldDex].FieldOid = CSSMOID_X509V3CertificateExtensionCStruct;
		}
		certTemp[fieldDex].FieldValue.Data = (uint8 *)ext;
		certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
	}
	if(fieldDex != numFields) {
		printf("CB_MakeCertTemplate numFields screwup\n");
		return NULL;
	}
	
	/*
	 * OK, here we go
	 */
	rawCert = (CSSM_DATA_PTR)CSSM_MALLOC(sizeof(CSSM_DATA));
	rawCert->Data = NULL;
	rawCert->Length = 0;
	CSSM_RETURN crtn = CSSM_CL_CertCreateTemplate(clHand,
		fieldDex,
		certTemp,
		rawCert);
	if(crtn) {
		printError("CSSM_CL_CertCreateTemplate", crtn);
		appFreeCssmData(rawCert, CSSM_TRUE);
		rawCert = NULL;
	}

	/* free the stuff we mallocd to get here */
	appFreeCssmData(serialDER, CSSM_TRUE);
	appFreeCssmData(versionDER, CSSM_TRUE);
	CSSM_FREE(certTemp);
	if((paramOid != NULL) && (algId.parameters.Data != NULL)) {
		CSSM_FREE(algId.parameters.Data);
	}
	return rawCert;
}