static int checkOneField(
	CSSM_CL_HANDLE		clHand,
	CSSM_HANDLE 		cacheHand1,
	CSSM_HANDLE 		cacheHand2,
	const CSSM_OID		*fieldOid)
{
	CSSM_DATA_PTR	fieldData1 = NULL;
	CSSM_DATA_PTR	fieldData2 = NULL;
	CSSM_RETURN		crtn;
	CSSM_HANDLE 	resultHand1 = 0;
	CSSM_HANDLE 	resultHand2 = 0;
	uint32 			numFields = 0;
	int				rtn;
	
	crtn = CSSM_CL_CertGetFirstCachedFieldValue(
		clHand,
		cacheHand1,
	    fieldOid,
	    &resultHand1,
	    &numFields,
		&fieldData1);
	if(crtn) {
		return crtn;
	}
	if(numFields != 1) {
		printf("Fiedl not present; try another cert\n");
		return 1;
	}
	crtn = CSSM_CL_CertGetFirstCachedFieldValue(
		clHand,
		cacheHand2,
	    fieldOid,
	    &resultHand2,
	    &numFields,
		&fieldData2);
	if(crtn) {
		return crtn;
	}
	rtn = compareFields(fieldOid, fieldData1, fieldData2);
  	CSSM_CL_CertAbortQuery(clHand, resultHand1);
  	CSSM_CL_CertAbortQuery(clHand, resultHand2);
	CSSM_CL_FreeFieldValue(clHand, fieldOid, fieldData1);
	CSSM_CL_FreeFieldValue(clHand, fieldOid, fieldData2);
	return rtn;
}
static void printOneCertName(
    CSSM_CL_HANDLE clHand,
    CSSM_HANDLE cacheHand,
    const char *title,
    const CSSM_OID *oid)
{
    CSSM_HANDLE resultHand = 0;
    CSSM_DATA_PTR field = NULL;
    uint32 numFields;
    CSSM_RETURN crtn;
    
    crtn = CSSM_CL_CertGetFirstCachedFieldValue(clHand, cacheHand,
	oid, &resultHand, &numFields, &field);
    if(crtn) {
	printf("***Error parsing cert\n");
	cssmPerror("CSSM_CL_CertGetFirstCachedFieldValue", crtn);
	return;
    }
    printName(title, field->Data, field->Length);
    CSSM_CL_FreeFieldValue(clHand, oid, field);
}
int main(int argc, char **argv)
{
	CSSM_CL_HANDLE		clHand;			// CL handle
	CSSM_DATA			rawCert = {0, NULL};	
	uint32				numFields;
	CSSM_HANDLE 		ResultsHandle = 0;
	char				baseName[255];
	char				blobName[255];
	char				*cp;
	int 				rtn;
	int					nameLen;
	CSSM_RETURN			crtn;
	CSSM_DATA_PTR		value;
	CSSM_KEY_PTR		key;
	uint32				keySize;
	NSS_Certificate 	signedCert;
	SecAsn1CoderRef		coder;
	
	if(argc != 2) {
		printf("usage: %s certFile\n", argv[0]);
		exit(1);
	}
	
	/* connect to CL */
	clHand = clStartup();
	if(clHand == 0) {
		printf("clStartup failure; aborting\n");
		return 0;
	}

	/* subsequent errors to abort: */
	/* read a in raw cert */
	unsigned len;
	rtn = readFile(argv[1], &rawCert.Data, &len);
	if(rtn) {
		printf("Error %s reading file %s\n", strerror(rtn), argv[1]);
		exit(1);
	}
	rawCert.Length = len;
	
	/* C string of file name, terminating at '.' or space */
	nameLen = strlen(argv[1]);
	memmove(baseName, argv[1], nameLen);
	baseName[nameLen] = '\0';
	cp = strchr(baseName, '.');
	if(cp) {
		*cp = '\0';
	}
	cp = strchr(baseName, ' ');
	if(cp) {
		*cp = '\0';
	}
	
	/* print filename and parsed subject name as comment */
	crtn = CSSM_CL_CertGetFirstFieldValue(
		clHand,
		&rawCert,
	    &CSSMOID_X509V1SubjectNameCStruct,
	    &ResultsHandle,
	    &numFields,
		&value);
	if(crtn) {
		printError("CSSM_CL_CertGetFirstFieldValue(CSSMOID_X509V1SubjectNameCStruct)", crtn);
  		goto abort;
	}
  	CSSM_CL_CertAbortQuery(clHand, ResultsHandle);
  	if(value == NULL) {
  		printf("Error extracting subject name\n");
  		goto abort;
  	}
	printHeader(argv[1], value);
  	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SubjectNameCStruct, value);

	/* print normalized & encoded subject name as C data */
	crtn = CSSM_CL_CertGetFirstFieldValue(
		clHand,
		&rawCert,
	    &SUBJECT_NAME_OID,
	    &ResultsHandle,
	    &numFields,
		&value);
	if(crtn) {
		printError("CSSM_CL_CertGetFirstFieldValue(CSSMOID_X509V1SubjectName)", crtn);
  		goto abort;
	}
  	CSSM_CL_CertAbortQuery(clHand, ResultsHandle);
  	if(value == NULL) {
  		printf("Error extracting subject name\n");
  		goto abort;
  	}
  	sprintf(blobName, "%s_subject", baseName);
  	dumpDataBlob(blobName, value);
	#if		WRITE_NAME_FILE
	writeFile(blobName, value->Data, (unsigned)value->Length);
	#endif
  	CSSM_CL_FreeFieldValue(clHand, &SUBJECT_NAME_OID, value);

	/* print key blob as data */
	crtn = CSSM_CL_CertGetFirstFieldValue(
		clHand,
		&rawCert,
		&CSSMOID_CSSMKeyStruct,
	    &ResultsHandle,
	    &numFields,
		&value);
	if(crtn) {
		printError("CSSM_CL_CertGetFirstFieldValue(CSSMOID_CSSMKeyStruct)", crtn);
  		goto abort;
	}
  	CSSM_CL_CertAbortQuery(clHand, ResultsHandle );
  	if(value == NULL) {
  		printf("Error extracting public key\n");
  		goto abort;
  	}
	if(value->Length != sizeof(CSSM_KEY)) {
		printf("CSSMOID_CSSMKeyStruct length error\n");
		goto abort;
	}
	key = (CSSM_KEY_PTR)value->Data;
	sprintf(blobName, "%s_pubKey", baseName);
  	dumpDataBlob(blobName, &key->KeyData);
	keySize = key->KeyHeader.LogicalKeySizeInBits;
  	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CSSMKeyStruct, value);
	
	/* unnormalized DER-encoded issuer */
	SecAsn1CoderCreate(&coder);
	memset(&signedCert, 0, sizeof(signedCert));
	if(SecAsn1DecodeData(coder, &rawCert, kSecAsn1SignedCertTemplate, &signedCert)) {
		printf("***Error NSS-decoding certificate\n");
	}
	else {
		sprintf(blobName, "%s_derIssuer", baseName);
		dumpDataBlob(blobName, &signedCert.tbs.derIssuer);
	}
	/* now the the struct containing all three */
	printf("\n    { &%s_subject, &%s_pubKey, %u },\n", baseName, baseName, (unsigned)keySize);
abort:
	free(rawCert.Data);
	if(clHand != 0) {
		CSSM_ModuleDetach(clHand);
	}
	SecAsn1CoderRelease(coder);
	return 0;
}
/* Free the field via CL */
CP_FetchedField::~CP_FetchedField()
{
	CSSM_CL_FreeFieldValue(mClHand, &mFieldOid, mFieldData);
}
CSSM_RETURN cuAddCrlToDb(
	CSSM_DL_DB_HANDLE	dlDbHand,
	CSSM_CL_HANDLE		clHand,
	const CSSM_DATA		*crl,
	const CSSM_DATA		*URI)		// optional
{
	CSSM_DB_ATTRIBUTE_DATA			attrs[MAX_CRL_ATTRS];
	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
	CSSM_DB_ATTRIBUTE_DATA_PTR		attr = &attrs[0];
	CSSM_DATA						crlTypeData;
	CSSM_DATA						crlEncData;
	CSSM_DATA						printNameData;
	CSSM_RETURN						crtn;
	CSSM_DB_UNIQUE_RECORD_PTR		recordPtr;
	CSSM_DATA_PTR					issuer = NULL;		// mallocd by CL
	CSSM_DATA_PTR					crlValue = NULL;	// ditto
	uint32							numFields;
	CSSM_HANDLE						result;
	CSSM_CRL_ENCODING 				crlEnc = CSSM_CRL_ENCODING_DER;
	const CSSM_X509_SIGNED_CRL 		*signedCrl;
	const CSSM_X509_TBS_CERTLIST 	*tbsCrl;
	CSSM_CRL_TYPE 					crlType;
	CSSM_DATA 						thisUpdateData = {0, NULL};
	CSSM_DATA 						nextUpdateData = {0, NULL};
	char							*thisUpdate = NULL;
	char							*nextUpdate = NULL;
	unsigned						timeLen;
	uint32							crlNumber;
	uint32							deltaCrlNumber;
	CSSM_DATA						crlNumberData;
	CSSM_DATA						deltaCrlNumberData;
	bool							crlNumberPresent = false;
	bool							deltaCrlPresent = false;
	CSSM_DATA						attrUri;
	
	/* get normalized issuer name as Issuer attr */
	crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
		crl,
		&CSSMOID_X509V1IssuerName,
		&result,
		&numFields,
		&issuer);
	if(crtn) {
		cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
		return crtn;
	}
	CSSM_CL_CrlAbortQuery(clHand, result);
	
	/* get parsed CRL from the CL */
	crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
		crl,
		&CSSMOID_X509V2CRLSignedCrlCStruct,
		&result,
		&numFields,
		&crlValue);
	if(crtn) {
		cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
		goto errOut;
	}
	CSSM_CL_CrlAbortQuery(clHand, result);
	if(crlValue == NULL) {
		dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
		crtn = CSSMERR_CL_INVALID_CRL_POINTER;
		goto errOut;
	}
	if((crlValue->Data == NULL) || 
	   (crlValue->Length != sizeof(CSSM_X509_SIGNED_CRL))) {
		dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
		crtn = CSSMERR_CL_INVALID_CRL_POINTER;
		goto errOut;
	}
	signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data;
	tbsCrl = &signedCrl->tbsCertList;
	
	/* CrlType inferred from version */
	if(tbsCrl->version.Length == 0) {
		/* should never happen... */
		crlType = CSSM_CRL_TYPE_X_509v1;
	}
	else {
		uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1];
		switch(vers) {
			case 0:
				crlType = CSSM_CRL_TYPE_X_509v1;
				break;
			case 1:
				crlType = CSSM_CRL_TYPE_X_509v2;
				break;
			default:
				dprintf("***Unknown version in CRL (%u)\n", vers);
				crlType = CSSM_CRL_TYPE_X_509v1;
				break;
		}
	}
	crlTypeData.Data = (uint8 *)&crlType;
	crlTypeData.Length = sizeof(CSSM_CRL_TYPE);
	/* encoding more-or-less assumed here */
	crlEncData.Data = (uint8 *)&crlEnc;
	crlEncData.Length = sizeof(CSSM_CRL_ENCODING);
	
	/* printName inferred from issuer */
	cuInferCrlLabel(&tbsCrl->issuer, &printNameData);
	
	/* cook up CSSM_TIMESTRING versions of this/next update */
	thisUpdate = cuX509TimeToCssmTimestring(&tbsCrl->thisUpdate, &timeLen);
	if(thisUpdate == NULL) {
		dprintf("***Badly formatted thisUpdate\n");
	}
	else {
		thisUpdateData.Data = (uint8 *)thisUpdate;
		thisUpdateData.Length = timeLen;
	}
	if(tbsCrl->nextUpdate.time.Data != NULL) {
		nextUpdate = cuX509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen);
		if(nextUpdate == NULL) {
			dprintf("***Badly formatted nextUpdate\n");
		}
		else {
			nextUpdateData.Data = (uint8 *)nextUpdate;
			nextUpdateData.Length = timeLen;
		}
	}
	else {
		/*
		 * NextUpdate not present; fake it by using "virtual end of time"
		 */
		CSSM_X509_TIME tempTime = {	0,		// timeType, not used
			{ strlen(CSSM_APPLE_CRL_END_OF_TIME), 
			  (uint8 *)CSSM_APPLE_CRL_END_OF_TIME} };
		nextUpdate = cuX509TimeToCssmTimestring(&tempTime, &timeLen);
		nextUpdateData.Data = (uint8 *)nextUpdate;
		nextUpdateData.Length = CSSM_TIME_STRLEN;
	}
	
	/* optional CrlNumber and DeltaCrlNumber */
	if(cuSearchNumericExtension(&tbsCrl->extensions,
			&CSSMOID_CrlNumber,
			&crlNumber)) {
		crlNumberData.Data = (uint8 *)&crlNumber;
		crlNumberData.Length = sizeof(uint32);
		crlNumberPresent = true;
	}
	if(cuSearchNumericExtension(&tbsCrl->extensions,
			&CSSMOID_DeltaCrlIndicator,
			&deltaCrlNumber)) {
		deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber;
		deltaCrlNumberData.Length = sizeof(uint32);
		deltaCrlPresent = true;
	}

	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr->Info.Label.AttributeName = (char*) "CrlType";
	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
	attr->NumberOfValues = 1;
	attr->Value = &crlTypeData;
	attr++;
	
	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr->Info.Label.AttributeName = (char*) "CrlEncoding";
	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
	attr->NumberOfValues = 1;
	attr->Value = &crlEncData;
	attr++;
	
	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr->Info.Label.AttributeName = (char*) "PrintName";
	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	attr->NumberOfValues = 1;
	attr->Value = &printNameData;
	attr++;
	
	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr->Info.Label.AttributeName = (char*) "Issuer";
	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	attr->NumberOfValues = 1;
	attr->Value = issuer;
	attr++;
	
	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr->Info.Label.AttributeName = (char*) "ThisUpdate";
	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	attr->NumberOfValues = 1;
	attr->Value = &thisUpdateData;
	attr++;
	
	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr->Info.Label.AttributeName = (char*) "NextUpdate";
	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	attr->NumberOfValues = 1;
	attr->Value = &nextUpdateData;
	attr++;
	
	/* now the optional attributes */
	if(crlNumberPresent) {
		attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
		attr->Info.Label.AttributeName = (char*) "CrlNumber";
		attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
		attr->NumberOfValues = 1;
		attr->Value = &crlNumberData;
		attr++;
	}
	if(deltaCrlPresent) {
		attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
		attr->Info.Label.AttributeName = (char*) "DeltaCrlNumber";
		attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
		attr->NumberOfValues = 1;
		attr->Value = &deltaCrlNumberData;
		attr++;
	}
	if(URI) {
		/* ensure URI string does not contain NULL */
		attrUri = *URI;
		if((attrUri.Length != 0) && 
		   (attrUri.Data[attrUri.Length - 1] == 0)) {
			attrUri.Length--;
		}
		attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
		attr->Info.Label.AttributeName = (char*) "URI";
		attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
		attr->NumberOfValues = 1;
		attr->Value = &attrUri;
		attr++;
	}
	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL;
	recordAttrs.SemanticInformation = 0;
	recordAttrs.NumberOfAttributes = (uint32)(attr - attrs);
	recordAttrs.AttributeData = attrs;
	
	crtn = CSSM_DL_DataInsert(dlDbHand,
		CSSM_DL_DB_RECORD_X509_CRL,
		&recordAttrs,
		crl,
		&recordPtr);
	if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
		/* gross hack of inserting this "new" schema that Keychain didn't specify */
		crtn = cuAddCrlSchema(dlDbHand);
		if(crtn == CSSM_OK) {
			/* Retry with a fully capable DLDB */
			crtn = CSSM_DL_DataInsert(dlDbHand,
				CSSM_DL_DB_RECORD_X509_CRL,
				&recordAttrs,
				crl,
				&recordPtr);
		}
	}
	if(crtn == CSSM_OK) {
		CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
	}
	
errOut:
	/* free all the stuff we allocated to get here */
	if(issuer) {
		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer);
	}
	if(crlValue) {
		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct, crlValue);
	}
	if(thisUpdate) {
		free(thisUpdate);
	}
	if(nextUpdate) {
		free(nextUpdate);
	}
	return crtn;
}
// Extract the issuer and serial number from a certificate
SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert)
{
    OSStatus status;
    SecCmsIssuerAndSN *certIssuerAndSN;
    CSSM_CL_HANDLE clHandle;
    CSSM_DATA_PTR serialNumber = 0;
    CSSM_DATA_PTR issuer = 0;
    CSSM_DATA certData = {};
    CSSM_HANDLE resultsHandle = 0;
    uint32 numberOfFields = 0;
    CSSM_RETURN result;
    void *mark;

    mark = PORT_ArenaMark(pl);

    status = SecCertificateGetCLHandle(cert, &clHandle);
    if (status)
	goto loser;
    status = SecCertificateGetData(cert, &certData);
    if (status)
	goto loser;

    /* Get the issuer from the cert. */
    result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, 
	&OID_X509V1IssuerNameStd, &resultsHandle, &numberOfFields, &issuer);

    if (result || numberOfFields < 1)
	goto loser;
    result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
    if (result)
	goto loser;


    /* Get the serialNumber from the cert. */
    result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, 
	&CSSMOID_X509V1SerialNumber, &resultsHandle, &numberOfFields, &serialNumber);
    if (result || numberOfFields < 1)
	goto loser;
    result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
    if (result)
	goto loser;

    /* Allocate the SecCmsIssuerAndSN struct. */
    certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN));
    if (certIssuerAndSN == NULL)
	goto loser;

    /* Copy the issuer. */
    certIssuerAndSN->derIssuer.Data = (uint8 *) PORT_ArenaAlloc(pl, issuer->Length);
    if (!certIssuerAndSN->derIssuer.Data)
	goto loser;
    PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer->Data, issuer->Length);
    certIssuerAndSN->derIssuer.Length = issuer->Length;

    /* Copy the serialNumber. */
    certIssuerAndSN->serialNumber.Data = (uint8 *) PORT_ArenaAlloc(pl, serialNumber->Length);
    if (!certIssuerAndSN->serialNumber.Data)
	goto loser;
    PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber->Data, serialNumber->Length);
    certIssuerAndSN->serialNumber.Length = serialNumber->Length;

    PORT_ArenaUnmark(pl, mark);

    CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber);
    CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer);

    return certIssuerAndSN;

loser:
    PORT_ArenaRelease(pl, mark);

    if (serialNumber)
	CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber);
    if (issuer)
	CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer);

    PORT_SetError(SEC_INTERNAL_ONLY);
    return NULL;
}
/*
 * Given a DER encoded certificate, obtain the associated IssuerAndSerialNumber.
 */
krb5_error_code krb5int_pkinit_get_issuer_serial(
    const krb5_data *cert,
    krb5_data       *issuer_and_serial)
{
    CSSM_HANDLE cacheHand = 0;
    CSSM_RETURN crtn = CSSM_OK;
    CSSM_DATA certData = { cert->length, (uint8 *)cert->data };
    CSSM_HANDLE resultHand = 0;
    CSSM_DATA_PTR derIssuer = NULL;
    CSSM_DATA_PTR serial;
    krb5_data krb_serial;
    krb5_data krb_issuer;
    uint32 numFields;
    krb5_error_code ourRtn = 0;

    CSSM_CL_HANDLE clHand = pkiClStartup();
    if(clHand == 0) {
	return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
    }
    /* subsequent errors to errOut: */

    crtn = CSSM_CL_CertCache(clHand, &certData, &cacheHand);
    if(crtn) {
	pkiCssmErr("CSSM_CL_CertCache", crtn);
	ourRtn = ASN1_PARSE_ERROR;
	goto errOut;
    }

    /* obtain the two fields; issuer is DER encoded */
    crtn = CSSM_CL_CertGetFirstCachedFieldValue(clHand, cacheHand,
	&CSSMOID_X509V1IssuerNameStd, &resultHand, &numFields, &derIssuer);
    if(crtn) {
	pkiCssmErr("CSSM_CL_CertGetFirstCachedFieldValue(issuer)", crtn);
	ourRtn = ASN1_PARSE_ERROR;
	goto errOut;
    }
    crtn = CSSM_CL_CertGetFirstCachedFieldValue(clHand, cacheHand,
	&CSSMOID_X509V1SerialNumber, &resultHand, &numFields, &serial);
    if(crtn) {
	pkiCssmErr("CSSM_CL_CertGetFirstCachedFieldValue(serial)", crtn);
	ourRtn = ASN1_PARSE_ERROR;
	goto errOut;
    }
    PKI_CSSM_TO_KRB_DATA(derIssuer, &krb_issuer);
    PKI_CSSM_TO_KRB_DATA(serial, &krb_serial);
    ourRtn = krb5int_pkinit_issuer_serial_encode(&krb_issuer, &krb_serial, issuer_and_serial);

errOut:
    if(derIssuer) {
	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerNameStd, derIssuer);
    }
    if(serial) {
	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SerialNumber, serial);
    }
    if(cacheHand) {
	CSSM_CL_CertAbortCache(clHand, cacheHand);
    }
    if(clHand) {
	pkiClDetachUnload(clHand);
    }
    return ourRtn;
}
void tp_CertFreeAlgId(
	CSSM_CL_HANDLE	clHand,
	CSSM_DATA_PTR	value)
{
  	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SignatureAlgorithm, value);
}
void tp_CertFreePublicKey(
	CSSM_CL_HANDLE	clHand,
	CSSM_DATA_PTR	value)
{
  	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SubjectPublicKeyCStruct, value);
}