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;
}
/*
 * Obtain atrbitrary field from cached cert. This class takes care of freeing
 * the field in its destructor. 
 *
 * Returns NULL if field not found (not exception). 
 *
 * Caller optionally specifies field length to check - specifying zero means
 * "don't care, don't check". Actual field length always returned in fieldLength. 
 */
const void *CertParser::fieldForOid(
	const CSSM_OID		&oid,
	CSSM_SIZE			&fieldLength)		// IN/OUT
{
	CSSM_RETURN crtn;
	
	uint32 NumberOfFields = 0;
	CSSM_HANDLE resultHand = 0;
	CSSM_DATA_PTR fieldData = NULL;

	assert(mClHand != 0);
	assert(mCacheHand != 0);
	crtn = CSSM_CL_CertGetFirstCachedFieldValue(
		mClHand,
		mCacheHand,
	    &oid,
	    &resultHand,
	    &NumberOfFields,
		&fieldData);
	if(crtn) {
		/* not an error; just means that the cert doesn't have this field */
		return NULL;
	}
	assert(NumberOfFields == 1);
  	CSSM_CL_CertAbortQuery(mClHand, resultHand);
	
	if(fieldLength) {
		if(fieldLength != fieldData->Length) {
			/* FIXME what's a good way to log in this situation? */
			printf("***CertParser::fieldForOid: field length mismatch\n");
			return NULL;
		}
	}
	/* Store the OID and the field for autorelease */
	CP_FetchedField *cpField = new CP_FetchedField(oid, fieldData, mClHand);
	mFetchedFields.push_back(cpField);
	fieldLength = fieldData->Length;
	return fieldData->Data;
}
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;
}
// 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;
}