signerAndSubj.cpp   [plain text]


/* Copyright (c) 1998,2003,2005-2006,2008 Apple Inc.
 *
 * signerAndSubj.c
 *
 * Create two certs - a root, and a subject cert signed by the root. Includes
 * extension construction. Verify certs every which way, including various expected
 * failures.
 *
 * Revision History
 * ----------------
 *  31 Aug 2000 Doug Mitchell at Apple
 *		Ported to X/CDSA2.
 *  20 Jul 1998	Doug Mitchell at NeXT
 *		Created.
 */

#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <clAppUtils/CertBuilderApp.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>

#define SUBJ_KEY_LABEL		"subjectKey"
#define ROOT_KEY_LABEL		"rootKey"
/* default key and signature algorithm */
#define SIG_ALG_DEFAULT		CSSM_ALGID_SHA1WithRSA
#define KEY_ALG_DEFAULT		CSSM_ALGID_RSA

/* for write certs/components option */
#define ROOT_CERT_FILE_NAME		"ssRootCert.der"
#define ROOT_TBS_FILE_NAME		"ssRootTBS.der"
#define SUBJ_CERT_FILE_NAME		"ssSubjCert.der"
#define SUBJ_TBS_FILE_NAME		"ssSubjTBS.der"
#define ROOT_PRIV_KEY_FILE		"ssRootPriv.der"
#define SUBJ_PRIV_KEY_FILE		"ssSubjPriv.der"

static void usage(char **argv)
{
	printf("Usage: %s [options]\n", argv[0]);
	printf("Options:\n");
	printf("    w[rite certs and components]\n");
	printf("    a=alg  where alg is s(RSA/SHA1), m(RSA/MD5), f(FEE/MD5), F(FEE/SHA1),\n");
	printf("           2(RSA/SHA224), 6(RSA/SHA256), 3(RSA/SHA384) 5=RSA/SHA512,\n");
	printf("           e(ECDSA), E(ANSI/ECDSA), 7(ECDSA/SHA256), 8(ECDSA/SHA384), 9(ECDSA/SHA512)\n");
	printf("    k=keySizeInBits\n");
	exit(1);
}

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

CB_NameOid subjRdn[] = 
{
	/* note extra space for normalize test */
	{ "Apple  Computer",				&CSSMOID_OrganizationName },
	{ "Doug Mitchell",					&CSSMOID_CommonName }
};
#define NUM_SUBJ_NAMES	(sizeof(subjRdn) / sizeof(CB_NameOid))

static CSSM_BOOL compareKeyData(const CSSM_KEY *key1, const CSSM_KEY *key2);
static CSSM_RETURN verifyCert(CSSM_CL_HANDLE clHand,
	CSSM_CSP_HANDLE	cspHand,
	CSSM_DATA_PTR	cert,
	CSSM_DATA_PTR	signerCert,
	CSSM_KEY_PTR	key,
	CSSM_ALGORITHMS	sigAlg,
	CSSM_RETURN		expectResult,
	const char 		*opString);


int main(int argc, char **argv)
{
	CSSM_CL_HANDLE	clHand;			// CL handle
	CSSM_X509_NAME	*subjName;
	CSSM_X509_NAME	*rootName;
	CSSM_X509_TIME	*notBefore;		// UTC-style "not before" time
	CSSM_X509_TIME	*notAfter;		// UTC-style "not after" time
	CSSM_DATA_PTR	rawCert;		// from CSSM_CL_CertCreateTemplate
	CSSM_DATA		signedRootCert;	// from CSSM_CL_CertSign
	CSSM_DATA		signedSubjCert;	// from CSSM_CL_CertSign
	CSSM_CSP_HANDLE	cspHand;		// CSP handle
	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;
	CSSM_KEY_PTR	extractRootKey;	// from CSSM_CL_CertGetKeyInfo()
	CSSM_KEY_PTR	extractSubjKey;	// ditto
	CSSM_CC_HANDLE	signContext;	// for signing/verifying the cert
	unsigned		badByte;
	int				arg;
	unsigned		errorCount = 0;
	
	/* user-spec'd variables */
	CSSM_BOOL		writeBlobs = CSSM_FALSE;
	CSSM_ALGORITHMS	keyAlg = KEY_ALG_DEFAULT;
	CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT;
	uint32			keySizeInBits = CSP_KEY_SIZE_DEFAULT;
	
	/* 
	 * Two extensions. Subject has one (KeyUsage); root has KeyUsage and 
	 * BasicConstraints.
	 */
	CSSM_X509_EXTENSION 	exts[2];
	CE_KeyUsage				keyUsage;
	CE_BasicConstraints		bc;
	
	for(arg=1; arg<argc; arg++) {
		switch(argv[arg][0]) {
			case 'w':
				writeBlobs = CSSM_TRUE;
				break;
			case 'a':
				if((argv[arg][1] == '\0') || (argv[arg][2] == '\0')) {
					usage(argv);
				}
				switch(argv[arg][2]) {
					case 's':
						keyAlg = CSSM_ALGID_RSA;
						sigAlg = CSSM_ALGID_SHA1WithRSA;
						break;
					case 'm':
						keyAlg = CSSM_ALGID_RSA;
						sigAlg = CSSM_ALGID_MD5WithRSA;
						break;
					case 'f':
						keyAlg = CSSM_ALGID_FEE;
						sigAlg = CSSM_ALGID_FEE_MD5;
						break;
					case 'F':
						keyAlg = CSSM_ALGID_FEE;
						sigAlg = CSSM_ALGID_FEE_SHA1;
						break;
					case 'e':
						keyAlg = CSSM_ALGID_FEE;
						sigAlg = CSSM_ALGID_SHA1WithECDSA;
						break;
					case 'E':
						keyAlg = CSSM_ALGID_ECDSA;
						sigAlg = CSSM_ALGID_SHA1WithECDSA;
						break;
					case '7':
						keyAlg = CSSM_ALGID_ECDSA;
						sigAlg = CSSM_ALGID_SHA256WithECDSA;
						break;
					case '8':
						keyAlg = CSSM_ALGID_ECDSA;
						sigAlg = CSSM_ALGID_SHA384WithECDSA;
						break;
					case '9':
						keyAlg = CSSM_ALGID_ECDSA;
						sigAlg = CSSM_ALGID_SHA512WithECDSA;
						break;
					case '2':
						keyAlg = CSSM_ALGID_RSA;
						sigAlg = CSSM_ALGID_SHA224WithRSA;
						break;
					case '6':
						keyAlg = CSSM_ALGID_RSA;
						sigAlg = CSSM_ALGID_SHA256WithRSA;
						break;
					case '3':
						keyAlg = CSSM_ALGID_RSA;
						sigAlg = CSSM_ALGID_SHA384WithRSA;
						break;
					case '5':
						keyAlg = CSSM_ALGID_RSA;
						sigAlg = CSSM_ALGID_SHA512WithRSA;
						break;
					default:
						usage(argv);
				}
				break;
		    case 'k':
				keySizeInBits = atoi(&argv[arg][2]);
				break;
			default:
				usage(argv);
		}
	}
	
	/* connect to CL and CSP */
	clHand = clStartup();
	if(clHand == 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,
		CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
		CSSM_KEYUSE_VERIFY,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		&subjPrivKey,
		CSSM_FALSE,			// privIsRef
		CSSM_KEYUSE_SIGN,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		CSSM_FALSE);
	if(crtn) {
		errorCount++;
		goto abort;
	}
	if(writeBlobs) {
		writeFile(SUBJ_PRIV_KEY_FILE, subjPrivKey.KeyData.Data,
			subjPrivKey.KeyData.Length);
		printf("...wrote %lu bytes to %s\n", subjPrivKey.KeyData.Length,
			SUBJ_PRIV_KEY_FILE);
	}

	/* 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_FALSE,			// privIsRef
		CSSM_KEYUSE_SIGN,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		CSSM_FALSE);
	if(crtn) {
		errorCount++;
		goto abort;
	}
	if(writeBlobs) {
		writeFile(ROOT_PRIV_KEY_FILE, rootPrivKey.KeyData.Data,
			rootPrivKey.KeyData.Length);
		printf("...wrote %lu bytes to %s\n", rootPrivKey.KeyData.Length,
			ROOT_PRIV_KEY_FILE);
	}

	if(compareKeyData(&rootPubKey, &subjPubKey)) {
	 	printf("**WARNING: Identical root and subj keys!\n");
	}

	/*
	 * Cook up various cert fields.
	 * First, the RDNs for subject and issuer. 
	 */
	rootName = CB_BuildX509Name(rootRdn, NUM_ROOT_NAMES);
	subjName = CB_BuildX509Name(subjRdn, NUM_SUBJ_NAMES);
	if((rootName == NULL) || (subjName == NULL)) {
		printf("CB_BuildX509Name failure");
		errorCount++;
		goto abort;
	}
	
	/* not before/after in generalized time format */
	notBefore = CB_BuildX509Time(0);
	notAfter  = CB_BuildX509Time(10000);

	/* A KeyUsage extension for both certs */
	exts[0].extnId = CSSMOID_KeyUsage;
	exts[0].critical = CSSM_FALSE;
	exts[0].format = CSSM_X509_DATAFORMAT_PARSED;
	keyUsage = CE_KU_DigitalSignature | CE_KU_KeyCertSign |
			   CE_KU_KeyEncipherment | CE_KU_DataEncipherment;
	exts[0].value.parsedValue = &keyUsage;
	exts[0].BERvalue.Data = NULL;
	exts[0].BERvalue.Length = 0;

	/* BasicConstraints for root only */
	exts[1].extnId = CSSMOID_BasicConstraints;
	exts[1].critical = CSSM_TRUE;
	exts[1].format = CSSM_X509_DATAFORMAT_PARSED;
	bc.cA = CSSM_TRUE;
	bc.pathLenConstraintPresent = CSSM_TRUE;
	bc.pathLenConstraint = 2;
	exts[1].value.parsedValue = &bc;
	exts[1].BERvalue.Data = NULL;
	exts[1].BERvalue.Length = 0;
	
	/* cook up root cert */
	printf("Creating root cert...\n");
	rawCert = CB_MakeCertTemplate(clHand,
		0x12345678,			// serial number
		rootName,
		rootName,
		notBefore,
		notAfter,
		&rootPubKey,
		sigAlg,
		NULL,				// subjUniqueId
		NULL,				// issuerUniqueId
		exts,				// extensions
		2);					// numExtensions

	if(rawCert == NULL) {
		errorCount++;
		goto abort;
	}
	if(writeBlobs) {
		writeFile(ROOT_TBS_FILE_NAME, rawCert->Data, rawCert->Length);
		printf("...wrote %lu bytes to %s\n", rawCert->Length, ROOT_TBS_FILE_NAME);
	}
	
	/* Self-sign; this is a root cert */
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
			sigAlg,
			NULL,			// AccessCred
			&rootPrivKey,
			&signContext);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext", crtn);
		errorCount++;
		goto abort;
	}
	signedRootCert.Data = NULL;
	signedRootCert.Length = 0;
	crtn = CSSM_CL_CertSign(clHand,
		signContext,
		rawCert,			// CertToBeSigned
		NULL,				// SignScope
		0,					// ScopeSize,
		&signedRootCert);
	if(crtn) {
		printError("CSSM_CL_CertSign", crtn);
		errorCount++;
		goto abort;
	}
	crtn = CSSM_DeleteContext(signContext);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		errorCount++;
		goto abort;
	}
	appFreeCssmData(rawCert, CSSM_TRUE);
	if(writeBlobs) {
		writeFile(ROOT_CERT_FILE_NAME, signedRootCert.Data, signedRootCert.Length);
		printf("...wrote %lu bytes to %s\n", signedRootCert.Length, 
			ROOT_CERT_FILE_NAME);
	}

	/* now a subject cert signed by the root cert */
	printf("Creating subject cert...\n");
	rawCert = CB_MakeCertTemplate(clHand,
		0x8765,				// serial number
		rootName,
		subjName,
		notBefore,
		notAfter,
		&subjPubKey,
		sigAlg,
		NULL,				// subjUniqueId
		NULL,				// issuerUniqueId
		exts,				// extensions
		1);					// numExtensions
	if(rawCert == NULL) {
		errorCount++;
		goto abort;
	}
	if(writeBlobs) {
		writeFile(SUBJ_TBS_FILE_NAME, rawCert->Data, rawCert->Length);
		printf("...wrote %lu bytes to %s\n", rawCert->Length, SUBJ_TBS_FILE_NAME);
	}

	/* sign by root */
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
			sigAlg,
			NULL,			// AccessCred
			&rootPrivKey,
			&signContext);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext", crtn);
		errorCount++;
		goto abort;
	}
	signedSubjCert.Data = NULL;
	signedSubjCert.Length = 0;
	crtn = CSSM_CL_CertSign(clHand,
		signContext,
		rawCert,			// CertToBeSigned
		NULL,				// SignScope
		0,					// ScopeSize,
		&signedSubjCert);
	if(crtn) {
		printError("CSSM_CL_CertSign", crtn);
		errorCount++;
		goto abort;
	}
	crtn = CSSM_DeleteContext(signContext);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		errorCount++;
		goto abort;
	}
	appFreeCssmData(rawCert, CSSM_TRUE);
	if(writeBlobs) {
		writeFile(SUBJ_CERT_FILE_NAME, signedSubjCert.Data, signedSubjCert.Length);
		printf("...wrote %lu bytes to %s\n", signedSubjCert.Length, 
			SUBJ_CERT_FILE_NAME);
	}

	/* Free the stuff we allocd to get here */
	CB_FreeX509Name(rootName);
	CB_FreeX509Name(subjName);
	CB_FreeX509Time(notBefore);
	CB_FreeX509Time(notAfter);

	/*
	 * Extract public keys from the two certs, verify.
	 */
	crtn = CSSM_CL_CertGetKeyInfo(clHand, &signedSubjCert, &extractSubjKey);
	if(crtn) {
		printError("CSSM_CL_CertGetKeyInfo", crtn);
	}
	else {
		/* compare key data - header is different.
		 * Known header differences:
		 *  -- CspID - CSSM_CL_CertGetKeyInfo returns a key with NULL for
		 *     this field
		 * --  Format. rootPubKey      : 6 (CSSM_KEYBLOB_RAW_FORMAT_BSAFE)
		 *             extractRootKey  : 1 (CSSM_KEYBLOB_RAW_FORMAT_PKCS1)
		 * --  KeyAttr. rootPubKey     : 0x20 (CSSM_KEYATTR_EXTRACTABLE)
		 *              extractRootKey : 0x0
		 */
		if(!compareKeyData(extractSubjKey, &subjPubKey)) {
			printf("***CSSM_CL_CertGetKeyInfo(signedSubjCert) returned bad key data\n");
		}
		if(extractSubjKey->KeyHeader.LogicalKeySizeInBits !=
				subjPubKey.KeyHeader.LogicalKeySizeInBits) {
			printf("***EffectiveKeySizeInBits mismatch: extract %u subj %u\n",
				(unsigned)extractSubjKey->KeyHeader.LogicalKeySizeInBits,
				(unsigned)subjPubKey.KeyHeader.LogicalKeySizeInBits);
		}
	}
	crtn = CSSM_CL_CertGetKeyInfo(clHand, &signedRootCert, &extractRootKey);
	if(crtn) {
		printError("CSSM_CL_CertGetKeyInfo", crtn);
	}
	else {
		if(!compareKeyData(extractRootKey, &rootPubKey)) {
			printf("***CSSM_CL_CertGetKeyInfo(signedRootCert) returned bad key data\n");
		}
	}

	/*
	 * Verify:
	 */
	printf("Verifying certificates...\n");
	
	/*
	 *  Verify root cert by root pub key, should succeed.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedRootCert,
			NULL,
			&rootPubKey,
			sigAlg,
			CSSM_OK,
			"Verify(root by root key)")) {
		errorCount++;
		/* continue */
	}
	
	/*
	 *  Verify root cert by root cert, should succeed.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedRootCert,
			&signedRootCert,
			NULL,
			CSSM_ALGID_NONE,			// sigAlg not used here
			CSSM_OK,
			"Verify(root by root cert)")) {
		errorCount++;
		/* continue */
	}


	/*
	 *  Verify subject cert by root pub key, should succeed.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			NULL,
			&rootPubKey,
			sigAlg,
			CSSM_OK,
			"Verify(subj by root key)")) {
		errorCount++;
		/* continue */
	}

	/*
	 *  Verify subject cert by root cert, should succeed.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			&signedRootCert,
			NULL,
			CSSM_ALGID_NONE,			// sigAlg not used here
			CSSM_OK,
			"Verify(subj by root cert)")) {
		errorCount++;
		/* continue */
	}

	/*
	 *  Verify subject cert by root cert AND key, should succeed.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			&signedRootCert,
			&rootPubKey,
			sigAlg,
			CSSM_OK,
			"Verify(subj by root cert and key)")) {
		errorCount++;
		/* continue */
	}

	/*
	 *  Verify subject cert by extracted root pub key, should succeed.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			NULL,
			extractRootKey,
			sigAlg,
			CSSM_OK,
			"Verify(subj by extracted root key)")) {
		errorCount++;
		/* continue */
	}

	/*
	 *  Verify subject cert by subject pub key, should fail.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			NULL,
			&subjPubKey,
			sigAlg,
			CSSMERR_CL_VERIFICATION_FAILURE,
			"Verify(subj by subj key)")) {
		errorCount++;
		/* continue */
	}

	/*
	 *  Verify subject cert by subject cert, should fail.
	 */
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			&signedSubjCert,
			NULL,
			CSSM_ALGID_NONE,			// sigAlg not used here
			CSSMERR_CL_VERIFICATION_FAILURE,
			"Verify(subj by subj cert)")) {
		errorCount++;
		/* continue */
	}

	/*
	 *  Verify erroneous subject cert by root pub key, should fail.
	 */
	badByte = genRand(1, signedSubjCert.Length - 1);
	signedSubjCert.Data[badByte] ^= 0x55;
	if(verifyCert(clHand,
			cspHand,
			&signedSubjCert,
			NULL,
			&rootPubKey,
			sigAlg,
			CSSMERR_CL_VERIFICATION_FAILURE,
			"Verify(bad subj by root key)")) {
		errorCount++;
		/* continue */
	}


	/* free/delete certs and keys */
	appFreeCssmData(&signedSubjCert, CSSM_FALSE);
	appFreeCssmData(&signedRootCert, CSSM_FALSE);

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

	/* These don't work because CSSM_CL_CertGetKeyInfo() gives keys with
	 * a bogus GUID. This may be a problem with the Apple CSP...
	 *
	cspFreeKey(cspHand, extractRootKey);
	cspFreeKey(cspHand, extractSubjKey);
	 *
	 * do it this way instead...*/
	CSSM_FREE(extractRootKey->KeyData.Data);
	CSSM_FREE(extractSubjKey->KeyData.Data);

	/* need to do this regardless...*/
	CSSM_FREE(extractRootKey);
	CSSM_FREE(extractSubjKey);

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

	if(errorCount) {
		printf("Signer/Subject test failed with %d errors\n", errorCount);
	}
	else {
		printf("Signer/Subject test succeeded\n");
	}
	return 0;
}


/* compare KeyData for two keys. */
static CSSM_BOOL compareKeyData(const CSSM_KEY *key1, const CSSM_KEY *key2)
{
	if(key1->KeyData.Length != key2->KeyData.Length) {
		return CSSM_FALSE;
	}
	if(memcmp(key1->KeyData.Data,
			key2->KeyData.Data,
			key1->KeyData.Length)) {
		return CSSM_FALSE;
	}
	return CSSM_TRUE;
}

/* verify a cert using specified key and/or signerCert */
static CSSM_RETURN verifyCert(CSSM_CL_HANDLE clHand,
	CSSM_CSP_HANDLE	cspHand,
	CSSM_DATA_PTR	cert,
	CSSM_DATA_PTR	signerCert,		// optional
	CSSM_KEY_PTR	key,			// ditto, to work spec one, other, or both
	CSSM_ALGORITHMS	sigAlg,			// CSSM_ALGID_SHA1WithRSA, etc. 
	CSSM_RETURN		expectResult,
	const char 		*opString)
{
	CSSM_RETURN		crtn;
	CSSM_CC_HANDLE	signContext = CSSM_INVALID_HANDLE;

	if(key) {
		crtn = CSSM_CSP_CreateSignatureContext(cspHand,
				sigAlg,
				NULL,				// AccessCred
				key,
				&signContext);
		if(crtn) {
			printf("Failure during %s\n", opString);
			printError("CSSM_CSP_CreateSignatureContext", crtn);
			return crtn;
		}
	}
	crtn = CSSM_CL_CertVerify(clHand,
		signContext,
		cert,					// CertToBeVerified
		signerCert,				// SignerCert
		NULL,					// VerifyScope 
		0);						// ScopeSize
	if(crtn != expectResult) {
		printf("Failure during %s\n", opString);
		if(crtn == CSSM_OK) {
			printf("Unexpected CSSM_CL_CertVerify success\n");
		}
		else if(expectResult == CSSM_OK) {
			printError("CSSM_CL_CertVerify", crtn);
		}
		else {
			printError("CSSM_CL_CertVerify: expected", expectResult);
			printError("CSSM_CL_CertVerify: got     ", crtn);
		}
		return CSSMERR_CL_VERIFICATION_FAILURE;
	}
	if(signContext != CSSM_INVALID_HANDLE) {
		crtn = CSSM_DeleteContext(signContext);
		if(crtn) {
			printf("Failure during %s\n", opString);
			printError("CSSM_DeleteContext", crtn);
			return crtn;
		}
	}
	return CSSM_OK;
}