view usr/src/lib/libkmf/libkmf/common/certop.c @ 10744:674514c28935

6887337 pktool gencert should use SHA1 instead of MD5
author Wyllys Ingersoll <wyllys.ingersoll@sun.com>
date Thu, 08 Oct 2009 14:02:27 -0700
parents d9be114e78c4
children 0e7751e37640
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <link.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <ber_der.h>
#include <kmfapiP.h>
#include <pem_encode.h>
#include <libgen.h>
#include <cryptoutil.h>

#define	CERTFILE_TEMPNAME	"/tmp/user.certXXXXXX"
#define	CRLFILE_TEMPNAME	"/tmp/crlXXXXXX"
#define	X509_FORMAT_VERSION 2

static KMF_RETURN
sign_cert(KMF_HANDLE_T, const KMF_DATA *, KMF_KEY_HANDLE *,
    KMF_OID *, KMF_DATA *);

static KMF_RETURN
verify_cert_with_key(KMF_HANDLE_T, KMF_DATA *, const KMF_DATA *);

static KMF_RETURN
verify_cert_with_cert(KMF_HANDLE_T, const KMF_DATA *, const KMF_DATA *);

static KMF_RETURN
get_sigalg_from_cert(KMF_DATA *, KMF_ALGORITHM_INDEX *);

static KMF_RETURN
get_keyalg_from_cert(KMF_DATA *cert, KMF_KEY_ALG *keyalg)
{
	KMF_RETURN rv;
	KMF_X509_CERTIFICATE *SignerCert = NULL;
	KMF_ALGORITHM_INDEX AlgorithmId;

	rv = DerDecodeSignedCertificate(cert, &SignerCert);

	if (rv != KMF_OK)
		return (rv);

	/* Get the algorithm info from the signer certificate */
	AlgorithmId = x509_algoid_to_algid(
	    &SignerCert->signature.algorithmIdentifier.algorithm);

	switch (AlgorithmId) {
		case KMF_ALGID_MD5WithRSA:
		case KMF_ALGID_MD2WithRSA:
		case KMF_ALGID_SHA1WithRSA:
			*keyalg = KMF_RSA;
			break;
		case KMF_ALGID_SHA1WithDSA:
			*keyalg = KMF_DSA;
			break;
		default:
			rv = KMF_ERR_BAD_ALGORITHM;
	}

	kmf_free_signed_cert(SignerCert);
	free(SignerCert);
	return (rv);
}

/*
 * Name: kmf_find_prikey_by_cert
 *
 * Description:
 *   This function finds the corresponding private key in keystore
 *   for a certificate
 */
KMF_RETURN
kmf_find_prikey_by_cert(KMF_HANDLE_T handle, int numattr,
    KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE kstype;
	KMF_KEY_ALG keyalg;
	KMF_KEY_HANDLE *key = NULL;
	KMF_DATA *cert = NULL;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
	    {KMF_KEY_HANDLE_ATTR, TRUE, sizeof (KMF_KEY_HANDLE),
	    sizeof (KMF_KEY_HANDLE)}
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	/*
	 * First, get the key algorithm info from the certificate and saves it
	 * in the returned key handle.
	 */
	cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr);
	if (cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	ret = get_keyalg_from_cert(cert, &keyalg);
	if (ret != KMF_OK)
		return (ret);

	key = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr);
	if (key == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	key->keyalg = keyalg;

	/* Call the plugin to do the work. */
	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
	    &kstype, NULL);
	if (ret != KMF_OK)
		return (ret);

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL || plugin->funclist->FindPrikeyByCert == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	return (plugin->funclist->FindPrikeyByCert(handle, numattr, attrlist));
}


KMF_RETURN
check_key_usage(void *handle,
	const KMF_DATA *cert,
	const KMF_KU_PURPOSE purpose)
{
	KMF_X509EXT_BASICCONSTRAINTS constraint;
	KMF_BOOL	critical = B_FALSE;
	KMF_X509EXT_KEY_USAGE keyusage;
	KMF_RETURN ret = KMF_OK;

	if (handle == NULL || cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	(void) memset(&constraint, 0, sizeof (KMF_X509EXT_BASICCONSTRAINTS));
	(void) memset(&keyusage, 0, sizeof (KMF_X509EXT_KEY_USAGE));

	ret = kmf_get_cert_ku(cert, &keyusage);
	if (ret != KMF_OK)
		/*
		 * If absent or error, the cert is assumed to be invalid
		 * for all key usage checking.
		 */
		return (ret);

	switch (purpose) {
	case KMF_KU_SIGN_CERT:
		/*
		 * RFC 3280:
		 * The keyCertSign bit is asserted when the subject
		 * public key is used for verifying a signature on
		 * public key certificates.  If the keyCertSign bit
		 * is asserted, then the cA bit in the basic constraints
		 * extension (section 4.2.1.10) MUST also be asserted.
		 * The basic constraints extension MUST appear as a
		 * critical extension in all CA certificates that
		 * contain public keys used to validate digital
		 * signatures on certificates.
		 */
		ret = kmf_get_cert_basic_constraint(cert, &critical,
		    &constraint);

		if ((ret != KMF_ERR_EXTENSION_NOT_FOUND) && (ret != KMF_OK)) {
			/* real error */
			return (ret);
		}

		if ((!critical) || (!constraint.cA) ||
		    (!(keyusage.KeyUsageBits & KMF_keyCertSign)))
			return (KMF_ERR_KEYUSAGE);
		break;
	case KMF_KU_SIGN_DATA:
		/*
		 * RFC 3280:
		 * The digitalSignature bit is asserted when the subject
		 * public key is used with a digital signature mechanism
		 * to support security services other than certificate
		 * signing(bit 5), or CRL signing(bit 6).
		 */
		if (!(keyusage.KeyUsageBits & KMF_digitalSignature))
			return (KMF_ERR_KEYUSAGE);
		break;
	case KMF_KU_ENCRYPT_DATA:
		/*
		 * RFC 3280:
		 * The dataEncipherment bit is asserted when the subject
		 * public key is used for enciphering user data, other than
		 * cryptographic keys.
		 */
		if (!(keyusage.KeyUsageBits & KMF_dataEncipherment))
			return (KMF_ERR_KEYUSAGE);
		break;
	default:
		return (KMF_ERR_BAD_PARAMETER);
	}

	return (KMF_OK);
}

KMF_RETURN
kmf_find_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE kstype;
	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_COUNT_ATTR, FALSE, sizeof (uint32_t), sizeof (uint32_t)}
	};
	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
	    &kstype, NULL);
	if (ret != KMF_OK)
		return (ret);

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL || plugin->funclist->FindCert == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	return (plugin->funclist->FindCert(handle, numattr, attrlist));
}

#define	NODATA(d) (d.Data == NULL || d.Length == NULL)

KMF_RETURN
kmf_encode_cert_record(KMF_X509_CERTIFICATE *CertData, KMF_DATA *encodedCert)
{
	KMF_RETURN ret;
	KMF_X509_TBS_CERT *tbs_cert;

	if (CertData == NULL || encodedCert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/*
	 * Validate that all required fields are present.
	 */
	tbs_cert = &(CertData->certificate);
	if (NODATA(tbs_cert->version) ||
	    NODATA(tbs_cert->signature.algorithm) ||
	    NODATA(tbs_cert->subjectPublicKeyInfo.subjectPublicKey) ||
	    tbs_cert->serialNumber.val == NULL ||
	    tbs_cert->serialNumber.len == 0 ||
	    tbs_cert->subject.numberOfRDNs == 0 ||
	    tbs_cert->issuer.numberOfRDNs == 0) {
		return (KMF_ERR_INCOMPLETE_TBS_CERT);
	}

	encodedCert->Length = 0;
	encodedCert->Data = NULL;

	/* Pack the new certificate */
	ret = DerEncodeSignedCertificate(CertData, encodedCert);

	return (ret);
}

/*
 * This function is used to setup the attribute list before calling
 * kmf_find_prikey_by_cert().  This function is used by
 *	kmf_decrypt_with_cert
 *	kmf_sign_cert
 *	kmf_sign_data
 *
 * The attribute list in these callers contain all the attributes
 * needed by kmf_find_prikey_by_cert(), except the
 * KMF_KEY_HANDLE attribute and the KMF_CERT_DATA_ATTR attribute.
 * These 2 attributes need to be added or reset.
 *
 * The caller should free the new_attrlist after use it.
 */
static KMF_RETURN
setup_findprikey_attrlist(KMF_ATTRIBUTE *src_attrlist, int src_num,
    KMF_ATTRIBUTE **new_attrlist, int *new_num, KMF_KEY_HANDLE *key,
    KMF_DATA *cert)
{
	KMF_ATTRIBUTE *attrlist = NULL;
	int cur_num = src_num;
	int index;
	int i;

	if (src_attrlist == NULL || new_num == NULL || key == NULL ||
	    cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/* Create a new attribute list with 2 more elements */
	attrlist = (KMF_ATTRIBUTE *) malloc(
	    (src_num + 2) * sizeof (KMF_ATTRIBUTE));
	if (attrlist == NULL)
		return (KMF_ERR_MEMORY);

	/* Copy the src_attrlist to the new list */
	for (i = 0; i < src_num; i++) {
		attrlist[i].type = src_attrlist[i].type;
		attrlist[i].pValue = src_attrlist[i].pValue;
		attrlist[i].valueLen = src_attrlist[i].valueLen;
	}

	/* Add or reset the key handle attribute */
	index = kmf_find_attr(KMF_KEY_HANDLE_ATTR, attrlist, cur_num);
	if (index == -1) {
		/* not found; add it */
		kmf_set_attr_at_index(attrlist, cur_num,
		    KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE));
		cur_num++;
	} else {
		/* found; just reset it */
		kmf_set_attr_at_index(attrlist, index,
		    KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE));
	}

	/* add or reset the cert data attribute */
	index = kmf_find_attr(KMF_CERT_DATA_ATTR, attrlist, cur_num);
	if (index == -1) {
		/* not found; add it */
		kmf_set_attr_at_index(attrlist, cur_num,
		    KMF_CERT_DATA_ATTR, cert, sizeof (KMF_DATA));
		cur_num++;
	} else {
		/* found; just reset it */
		kmf_set_attr_at_index(attrlist, index,
		    KMF_CERT_DATA_ATTR, cert, sizeof (KMF_DATA));
	}

	*new_attrlist = attrlist;
	*new_num = cur_num;
	return (KMF_OK);
}


/*
 * Name: kmf_sign_cert
 *
 * Description:
 *   This function signs a certificate using the signer cert and
 *   returns a signed and DER-encoded certificate.
 *
 * The following types of certificate data can be submitted to be signed:
 *	KMF_TBS_CERT_DATA_ATTR - a KMF_DATA ptr is provided in the attrlist
 *		and is signed directly.
 *	KMF_X509_CERTIFICATE_ATTR - a KMF_X509_CERTIFICATE record is provided
 *		in the attribute list.  This is converted to raw KMF_DATA
 *		prior to signing.
 *
 * The key for the signing operation can be provided as a KMF_KEY_HANDLE_ATTR
 * or the caller may choose to provide a KMF_SIGNER_CERT_ATTR (KMF_DATA *).
 * If the latter, this function will then attempt to find the private key
 * associated with the certificate.  The private key must be stored in
 * the same keystore as the signer certificate.
 */
KMF_RETURN
kmf_sign_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_RETURN ret;
	int new_numattr = numattr + 1;
	KMF_ATTRIBUTE *new_attrlist = NULL;
	KMF_DATA *signer_cert = NULL;
	KMF_DATA *tbs_cert = NULL;  /* to be signed cert */
	KMF_DATA *signed_cert = NULL;
	KMF_DATA unsignedCert = {NULL, 0};
	KMF_KEY_HANDLE sign_key, *sign_key_ptr;
	int freethekey = 0;
	KMF_POLICY_RECORD *policy;
	KMF_OID *oid = NULL;
	KMF_ALGORITHM_INDEX AlgId;
	KMF_X509_CERTIFICATE *x509cert;
	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}
	};
	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	/* Get the signer cert and check its keyUsage */
	signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
	    numattr);
	sign_key_ptr = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist,
	    numattr);
	/*
	 * Only accept 1 or the other, not both.
	 */
	if (signer_cert == NULL && sign_key_ptr == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	if (signer_cert != NULL && sign_key_ptr != NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (signer_cert != NULL) {
		policy = handle->policy;
		ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_CERT);
		if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
			ret = KMF_OK;
		if (ret != KMF_OK)
			return (ret);

		/*
		 * Find the private key from the signer certificate by calling
		 * kmf_find_prikey_by_cert().
		 */
		ret = setup_findprikey_attrlist(attrlist, numattr,
		    &new_attrlist, &new_numattr, &sign_key, signer_cert);
		if (ret != KMF_OK)
			goto out;

		ret = kmf_find_prikey_by_cert(handle, new_numattr,
		    new_attrlist);
		if (ret != KMF_OK) {
			goto out;
		}
		sign_key_ptr = &sign_key;
		freethekey = 1;

		ret = get_sigalg_from_cert(signer_cert, &AlgId);
		if (ret != KMF_OK)
			goto out;
		else
			oid = x509_algid_to_algoid(AlgId);
	} else if (sign_key_ptr != NULL) {
		if (sign_key_ptr->keyalg == KMF_RSA) {
			oid = (KMF_OID *)&KMFOID_SHA1WithRSA;
		} else if (sign_key_ptr->keyalg == KMF_DSA) {
			oid = (KMF_OID *)&KMFOID_SHA1WithDSA;
		}
	}

	/* Now we are ready to sign */
	tbs_cert = kmf_get_attr_ptr(KMF_TBS_CERT_DATA_ATTR, attrlist,
	    numattr);
	if (tbs_cert == NULL) {
		x509cert = kmf_get_attr_ptr(KMF_X509_CERTIFICATE_ATTR, attrlist,
		    numattr);
		if (x509cert == NULL) {
			ret = KMF_ERR_BAD_PARAMETER;
			goto out;
		}
		ret = kmf_encode_cert_record(x509cert, &unsignedCert);
		if (ret == KMF_OK)
			tbs_cert = &unsignedCert;
		else
			goto out;
	}

	signed_cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
	    numattr);
	if (signed_cert == NULL) {
		ret = KMF_ERR_BAD_PARAMETER;
		goto out;
	}

	ret = sign_cert(handle, tbs_cert, sign_key_ptr, oid, signed_cert);

out:
	if (new_attrlist)
		(void) free(new_attrlist);

	/* If we had to find the key, free it here. */
	if (freethekey)
		kmf_free_kmf_key(handle, &sign_key);

	kmf_free_data(&unsignedCert);
	return (ret);
}

static KMF_RETURN
get_sigalg_from_cert(KMF_DATA *signer_cert, KMF_ALGORITHM_INDEX *AlgId)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_CERTIFICATE *x509_cert = NULL;
	KMF_OID *oid;

	*AlgId = KMF_ALGID_NONE;

	/* if no OID and no AlgID, use the signer cert */
	ret = DerDecodeSignedCertificate(signer_cert, &x509_cert);
	if (ret != KMF_OK)
		return (ret);

	oid = CERT_ALG_OID(x509_cert);
	*AlgId = x509_algoid_to_algid(oid);

	if (*AlgId == KMF_ALGID_NONE)
		ret = KMF_ERR_BAD_PARAMETER;

	if (x509_cert != NULL) {
		kmf_free_signed_cert(x509_cert);
		free(x509_cert);
	}
	return (ret);
}

/*
 * Name: kmf_sign_data
 *
 * Description:
 *   This function signs a block of data using the signer cert and
 *   returns the the signature in output
 */
KMF_RETURN
kmf_sign_data(KMF_HANDLE_T handle, int numattr,
    KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_ATTRIBUTE *new_attrlist = NULL;
	int new_numattr = numattr;
	KMF_DATA *signer_cert = NULL;
	KMF_DATA *tbs_data = NULL;  /* to be signed data */
	KMF_DATA *output = NULL;
	KMF_KEY_HANDLE sign_key, *sign_key_ptr;
	KMF_ALGORITHM_INDEX AlgId;
	KMF_DATA	signature = {0, NULL};
	KMF_OID *oid;
	KMF_POLICY_RECORD *policy;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
	    {KMF_OUT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}
	};
	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	/* Get the signer cert and check its keyUsage. */
	signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
	    numattr);
	sign_key_ptr = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist,
	    numattr);

	if (signer_cert == NULL && sign_key_ptr == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/*
	 * If a signer cert was given, use it to find the private key
	 * to use for signing the data.
	 */
	if (signer_cert != NULL) {
		ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_DATA);

		/*
		 * Signing generic data does not require the
		 * KeyUsage extension.
		 */
		policy = handle->policy;
		if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
			ret = KMF_OK;
		if (ret != KMF_OK)
			return (ret);

		/*
		 * Find the private key from the signer certificate.
		 */
		ret = setup_findprikey_attrlist(attrlist, numattr,
		    &new_attrlist, &new_numattr, &sign_key, signer_cert);
		if (ret != KMF_OK) {
			goto cleanup;
		}

		ret = kmf_find_prikey_by_cert(handle, new_numattr,
		    new_attrlist);
		if (ret != KMF_OK) {
			goto cleanup;
		}
		sign_key_ptr = &sign_key;
	}

	/* Get the tbs_data and signed_data attributes now */
	tbs_data = kmf_get_attr_ptr(KMF_DATA_ATTR, attrlist, numattr);
	if (tbs_data == NULL) {
		ret = KMF_ERR_BAD_PARAMETER;
		goto cleanup;
	}

	output = kmf_get_attr_ptr(KMF_OUT_DATA_ATTR, attrlist, numattr);
	if (output == NULL) {
		ret = KMF_ERR_BAD_PARAMETER;
		goto cleanup;
	}

	/*
	 * Get the algorithm index attribute and its oid. If this attribute
	 * is not provided, then we use the algorithm in the signer cert.
	 */
	oid = kmf_get_attr_ptr(KMF_OID_ATTR, attrlist, numattr);
	ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, numattr,
	    &AlgId, NULL);
	/*
	 * We need to know the Algorithm ID, it can be found 3 ways:
	 * 1. caller supplied OID in the attribute list.
	 * 2. caller supplied Algorithm Index in the attribute list.
	 * 3. caller supplied neither, but did supply a certificate, find
	 *    the ALG OID from the certificate.
	 */
	/* If none of the above, return error. */
	if (oid == NULL && ret != KMF_OK && signer_cert == NULL) {
		ret = KMF_ERR_BAD_PARAMETER;
		goto cleanup;
	} else if (oid == NULL && ret != KMF_OK) {
		ret = get_sigalg_from_cert(signer_cert, &AlgId);
		if (ret != KMF_OK)
			goto cleanup;
		else
			oid = x509_algid_to_algoid(AlgId);

	} else if (oid == NULL && ret == KMF_OK) {
		/* AlgID was given by caller, convert it to OID */
		oid = x509_algid_to_algoid(AlgId);
	} else if (oid != NULL && ret == KMF_ERR_ATTR_NOT_FOUND) {
		AlgId = x509_algoid_to_algid(oid);
	} else { /* Else, the OID must have been given */
		ret = KMF_OK;
	}

	/* Now call the plugin function to sign it */
	plugin = FindPlugin(handle, sign_key_ptr->kstype);
	if (plugin == NULL || plugin->funclist->SignData == NULL) {
		ret = KMF_ERR_PLUGIN_NOTFOUND;
		goto cleanup;
	}

	ret = plugin->funclist->SignData(handle, sign_key_ptr, oid, tbs_data,
	    output);
	if (ret != KMF_OK)
		goto cleanup;

	/*
	 * For DSA, NSS returns an encoded signature. Decode the
	 * signature as DSA signature should be 40-byte long.
	 */
	if (plugin->type == KMF_KEYSTORE_NSS &&
	    AlgId == KMF_ALGID_SHA1WithDSA) {
		ret = DerDecodeDSASignature(output, &signature);
		if (ret != KMF_OK)
			goto cleanup;
		output->Length = signature.Length;
		(void) memcpy(output->Data, signature.Data, signature.Length);
	}

cleanup:
	if (new_attrlist != NULL)
		free(new_attrlist);

	if (signature.Data)
		free(signature.Data);

	if (signer_cert != NULL && sign_key_ptr != NULL)
		kmf_free_kmf_key(handle, sign_key_ptr);


	return (ret);
}

/*
 * kmf_verify_data
 *
 * This routine will try to verify a block of data using
 * either a public key or a certificate as the source
 * of the verification (the key).
 *
 * The caller may provider either a KMF_KEY_HANDLE_ATTR or
 * a KMF_SIGNER_CERT_DATA_ATTR (with a KMF_DATA record) to
 * use for the key to the verification step.  If a certificate
 * is used and that certificate has the KeyUsage extension,
 * the SIGN-DATA bit must be set.  Also, if a certificate
 * is used, the verification will be done in a specific
 * keystore mechanism.
 *
 * If a KMF_KEY_HANDLE is given in the attribute list, the
 * verification will occur in the framework itself using
 * PKCS#11 C_Verify functions.
 */
KMF_RETURN
kmf_verify_data(KMF_HANDLE_T handle,
	int	num_args,
	KMF_ATTRIBUTE	*attrlist)
{
	KMF_RETURN ret = KMF_OK;
	KMF_PLUGIN *plugin;
	KMF_KEYSTORE_TYPE kstype;
	uint32_t len;
	KMF_DATA	derkey = {0, NULL};
	KMF_KEY_HANDLE *KMFKey;
	KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_NONE;
	KMF_DATA *indata;
	KMF_DATA *insig;
	KMF_DATA *signer_cert;
	KMF_X509_SPKI spki;
	KMF_POLICY_RECORD *policy;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
		{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
		{KMF_DATA_ATTR, FALSE, sizeof (KMF_DATA),
			sizeof (KMF_DATA)},
		{KMF_IN_SIGN_ATTR, FALSE, sizeof (KMF_DATA),
			sizeof (KMF_DATA)}
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, num_args, attrlist);

	if (ret != KMF_OK)
		return (ret);

	len = sizeof (kstype);
	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, num_args,
	    &kstype, &len);
	if (ret != KMF_OK)
		return (ret);

	KMFKey = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, num_args);
	signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
	    num_args);
	if (KMFKey == NULL && signer_cert == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	len = sizeof (sigAlg);
	ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, num_args,
	    &sigAlg, &len);

	/* We only need the algorithm index if we don't have a signer cert. */
	if (ret != KMF_OK && signer_cert == NULL)
		return (ret);

	indata = kmf_get_attr_ptr(KMF_DATA_ATTR, attrlist, num_args);
	if (indata == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	insig = kmf_get_attr_ptr(KMF_IN_SIGN_ATTR, attrlist, num_args);
	if (insig == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/* If the caller passed a signer cert instead of a key use it. */
	if (signer_cert != NULL) {
		policy = handle->policy;
		ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_DATA);
		if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
			ret = KMF_OK;
		if (ret != KMF_OK)
			return (ret);

		if (kstype == KMF_KEYSTORE_NSS)
			kstype = KMF_KEYSTORE_PK11TOKEN;
		plugin = FindPlugin(handle, kstype);
		if (plugin == NULL)
			return (KMF_ERR_PLUGIN_NOTFOUND);
		if (plugin->funclist->VerifyDataWithCert == NULL)
			return (KMF_ERR_FUNCTION_NOT_FOUND);

		CLEAR_ERROR(handle, ret);
		ret = plugin->funclist->VerifyDataWithCert(handle,
		    sigAlg, indata, insig, signer_cert);
	} else {
		/* Retrieve public key data from keystore */
		plugin = FindPlugin(handle, kstype);
		if (plugin != NULL &&
		    plugin->funclist->EncodePubkeyData != NULL) {
			ret = plugin->funclist->EncodePubkeyData(handle,
			    KMFKey, &derkey);
		} else {
			return (KMF_ERR_PLUGIN_NOTFOUND);
		}

		ret = DerDecodeSPKI(&derkey, &spki);
		if (ret == KMF_OK) {
			ret = PKCS_VerifyData(handle, sigAlg, &spki,
			    indata, insig);
		}

		if (derkey.Data != NULL)
			free(derkey.Data);

		kmf_free_algoid(&spki.algorithm);
		kmf_free_data(&spki.subjectPublicKey);
	}

	return (ret);
}
/*
 * Name: kmf_verify_cert
 *
 * Description:
 *   This function verifies that the a certificate was signed
 * using a specific private key and that the certificate has not
 * been altered since it was signed using that private key
 * The public key used for verification may be given in the
 * attribute list as a KMF_KEY_HANDLE or the caller may give
 * just the signing certificate (as KMF_SIGNER_CERT_DATA_ATTR)
 * from which the public key needed for verification can be
 * derived.
 *
 * Parameters:
 *	handle(input) - opaque handle for KMF session
 *	numattr  - number of attributes in the list
 *	attrlist - KMF_ATTRIBUTES
 *
 * Returns:
 *   A KMF_RETURN value indicating success or specifying a particular
 * error condition.  The value KMF_OK indicates success. All other
 * values represent an error condition.
 */
KMF_RETURN
kmf_verify_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_RETURN	ret;
	KMF_DATA	derkey = {0, NULL};
	KMF_PLUGIN	*plugin;
	KMF_KEY_HANDLE *KMFKey;
	KMF_DATA *CertToBeVerified;
	KMF_DATA *SignerCert;
	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	CLEAR_ERROR(handle, ret);
	if (ret != KMF_OK)
		return (ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	KMFKey = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr);
	SignerCert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
	    numattr);

	/*
	 * Caller must provide at least a key handle or a cert to use
	 * as the "key" for verification.
	 */
	if (KMFKey == NULL && SignerCert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CertToBeVerified = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
	    numattr);
	if (CertToBeVerified == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (SignerCert != NULL) {
		ret = verify_cert_with_cert(handle, CertToBeVerified,
		    SignerCert);
	} else {
		/*
		 * The keystore must extract the pubkey data because
		 * the framework doesn't have access to the raw key bytes
		 * that are needed to construct the DER encoded public
		 * key information needed for the verify operation.
		 */
		plugin = FindPlugin(handle, KMFKey->kstype);
		if (plugin != NULL && plugin->funclist->EncodePubkeyData !=
		    NULL) {
			ret = plugin->funclist->EncodePubkeyData(handle,
			    KMFKey, &derkey);
		} else {
			return (KMF_ERR_PLUGIN_NOTFOUND);
		}

		if (ret == KMF_OK && derkey.Length > 0) {
			ret = verify_cert_with_key(handle, &derkey,
			    CertToBeVerified);

			if (derkey.Data != NULL)
				free(derkey.Data);
		}
	}

	return (ret);
}

/*
 * Utility routine for verifying generic data using a
 * certificate to derive the public key.  This is
 * done in a specific plugin because there are situations
 * where we want to force this operation to happen in
 * a specific keystore.
 * For example:
 *    libelfsign.so.1 verifies signatures on crypto libraries.
 *    We must use pkcs11 functions to verify the pkcs11
 *    plugins in order to keep the validation within the
 *    Cryptographic Framework's FIPS-140 boundary. To avoid
 *    a circular dependency, pksc11_softtoken.so.1 is
 *    interposed by libkcfd.so.1 via kcfd, which prevents
 *    libpkcs11.so.1's interfaces from being used when libkmf.so.1
 *    is called from kcfd.
 */
static KMF_RETURN
plugin_verify_data_with_cert(KMF_HANDLE_T handle,
	KMF_KEYSTORE_TYPE kstype,
	KMF_ALGORITHM_INDEX algid,
	KMF_DATA *indata,
	KMF_DATA *insig,
	const KMF_DATA *SignerCert)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;

	/*
	 * If NSS, use PKCS#11, we are not accessing the database(s),
	 * we just prefer the "verify" operation from the crypto framework.
	 */
	if (kstype == KMF_KEYSTORE_NSS)
		kstype = KMF_KEYSTORE_PK11TOKEN;

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	if (plugin->funclist->VerifyDataWithCert == NULL)
		return (KMF_ERR_FUNCTION_NOT_FOUND);

	CLEAR_ERROR(handle, ret);
	ret = (plugin->funclist->VerifyDataWithCert(handle,
	    algid, indata, insig, (KMF_DATA *)SignerCert));

	return (ret);
}

/*
 * Name: kmf_encrypt
 *
 * Description:
 *   Uses the public key from the cert to encrypt the plaintext
 *   into the ciphertext.
 *
 * Parameters:
 *   handle(input) - opaque handle for KMF session
 *   cert(input) - pointer to a DER encoded certificate for encryption
 *		by using its public key
 *   plaintext(input) - pointer to the plaintext to be encrypted
 *   ciphertext(output) - pointer to the ciphertext contains
 *		encrypted data
 *
 * Returns:
 *   A KMF_RETURN value indicating success or specifying a particular
 * error condition.
 *   The value KMF_OK indicates success. All other values represent
 * an error condition.
 *
 */
KMF_RETURN
kmf_encrypt(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_RETURN ret;
	KMF_X509_CERTIFICATE *x509cert = NULL;
	KMF_X509_SPKI *pubkey;
	KMF_OID *alg;
	KMF_ALGORITHM_INDEX algid;
	KMF_DATA *cert;
	KMF_DATA *plaintext;
	KMF_DATA *ciphertext;
	KMF_POLICY_RECORD *policy;
	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
		sizeof (KMF_DATA)},
	    {KMF_PLAINTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
		sizeof (KMF_DATA)},
	    {KMF_CIPHERTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
		sizeof (KMF_DATA)}
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	CLEAR_ERROR(handle, ret);
	if (ret != KMF_OK)
		return (ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
	    numattr);
	plaintext = kmf_get_attr_ptr(KMF_PLAINTEXT_DATA_ATTR, attrlist,
	    numattr);
	ciphertext = kmf_get_attr_ptr(KMF_CIPHERTEXT_DATA_ATTR, attrlist,
	    numattr);

	if (cert == NULL || plaintext == NULL || ciphertext == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/* check the keyUsage of the certificate */
	policy = handle->policy;
	ret = check_key_usage(handle, cert, KMF_KU_ENCRYPT_DATA);
	if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
		ret = KMF_OK;
	if (ret != KMF_OK)
		return (ret);

	/* Decode the cert so we can get the SPKI data */
	if ((ret = DerDecodeSignedCertificate(cert, &x509cert)) != KMF_OK)
		return (ret);

	/* Get the public key info from the certificate */
	pubkey = &x509cert->certificate.subjectPublicKeyInfo;

	/* Use the algorithm in SPKI to encrypt data */
	alg = &pubkey->algorithm.algorithm;

	algid = x509_algoid_to_algid(alg);

	/* DSA does not support encrypt */
	if (algid == KMF_ALGID_DSA || algid == KMF_ALGID_NONE) {
		kmf_free_signed_cert(x509cert);
		free(x509cert);
		return (KMF_ERR_BAD_ALGORITHM);
	}

	/*
	 * Encrypt using the crypto framework (not the KMF plugin mechanism).
	 */
	ret = PKCS_EncryptData(handle, algid, pubkey, plaintext, ciphertext);

	kmf_free_signed_cert(x509cert);
	free(x509cert);

	return (ret);
}

/*
 * Name: kmf_decrypt
 *
 * Description:
 *   Uses the private key associated with the cert to decrypt
 *   the ciphertext into the plaintext.
 */
KMF_RETURN
kmf_decrypt(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_RETURN ret;
	KMF_X509_CERTIFICATE *x509cert = NULL;
	KMF_X509_SPKI *spki_ptr;
	KMF_PLUGIN *plugin;
	KMF_ALGORITHM_INDEX AlgorithmId;
	KMF_ATTRIBUTE *new_attrlist = NULL;
	int new_numattr;
	KMF_DATA *cert = NULL;
	KMF_DATA *ciphertext = NULL;
	KMF_DATA *plaintext = NULL;
	KMF_KEY_HANDLE prikey;
	KMF_POLICY_RECORD *policy;
	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
	    {KMF_PLAINTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
		sizeof (KMF_DATA)},
	    {KMF_CIPHERTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
		sizeof (KMF_DATA)},
	};
	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);


	/* Get the cert and check its keyUsage */
	cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
	    numattr);
	if (cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/* check the keyUsage of the certificate */
	policy = handle->policy;
	ret = check_key_usage(handle, cert, KMF_KU_ENCRYPT_DATA);
	if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
		ret = KMF_OK;
	if (ret != KMF_OK)
		return (ret);

	/* Get the ciphertext and plaintext attributes */
	ciphertext = kmf_get_attr_ptr(KMF_CIPHERTEXT_DATA_ATTR, attrlist,
	    numattr);
	if (ciphertext == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	plaintext = kmf_get_attr_ptr(KMF_PLAINTEXT_DATA_ATTR, attrlist,
	    numattr);
	if (plaintext == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/*
	 * Retrieve the private key from the keystore based on
	 * the certificate.
	 */
	ret = setup_findprikey_attrlist(attrlist, numattr, &new_attrlist,
	    &new_numattr, &prikey, cert);
	if (ret != KMF_OK)
		goto cleanup;

	ret = kmf_find_prikey_by_cert(handle, new_numattr, new_attrlist);
	if (ret != KMF_OK)
		goto cleanup;

	/* Decode the cert so we can get the alogorithm */
	ret = DerDecodeSignedCertificate(cert, &x509cert);
	if (ret != KMF_OK)
		goto cleanup;

	spki_ptr = &x509cert->certificate.subjectPublicKeyInfo;
	AlgorithmId = x509_algoid_to_algid((KMF_OID *)
	    &spki_ptr->algorithm.algorithm);

	/* DSA does not support decrypt */
	if (AlgorithmId == KMF_ALGID_DSA) {
		ret = KMF_ERR_BAD_ALGORITHM;
		goto cleanup;
	}

	plugin = FindPlugin(handle, prikey.kstype);

	if (plugin != NULL && plugin->funclist->DecryptData != NULL) {
		ret = plugin->funclist->DecryptData(handle,
		    &prikey, &spki_ptr->algorithm.algorithm,
		    ciphertext, plaintext);
	} else {
		ret = KMF_ERR_PLUGIN_NOTFOUND;
	}

cleanup:
	if (new_attrlist != NULL)
		free(new_attrlist);

	kmf_free_kmf_key(handle, &prikey);
	kmf_free_signed_cert(x509cert);
	free(x509cert);

	return (ret);
}

KMF_RETURN
kmf_store_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE kstype;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
	    &kstype, NULL);
	if (ret != KMF_OK)
		return (ret);

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL || plugin->funclist->StoreCert == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	return (plugin->funclist->StoreCert(handle, numattr, attrlist));
}

KMF_RETURN
kmf_import_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE kstype;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_CERT_FILENAME_ATTR, TRUE, 1, 0},
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs, 0, NULL,
	    numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
	    &kstype, NULL);
	if (ret != KMF_OK)
		return (ret);

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL || plugin->funclist->ImportCert == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	return (plugin->funclist->ImportCert(handle, numattr, attrlist));
}

KMF_RETURN
kmf_delete_cert_from_keystore(KMF_HANDLE_T handle, int numattr,
    KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE kstype;
	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}
	};
	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
	    &kstype, NULL);
	if (ret != KMF_OK)
		return (ret);

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL || plugin->funclist->DeleteCert == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	return (plugin->funclist->DeleteCert(handle, numattr, attrlist));
}


/*
 * This function gets the CRL URI entries from the certificate's Distribution
 * points extension, and downloads the CRL file.  The function also returns
 * the URI string and the format of the CRL file.   The caller should free
 * the space allocated for the returned URI string.
 */
static KMF_RETURN
cert_get_crl(KMF_HANDLE_T handle, const KMF_DATA *cert, char *proxy,
    char *filename, char **retn_uri, KMF_ENCODE_FORMAT *format)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509EXT_CRLDISTPOINTS crl_dps;
	boolean_t done = B_FALSE;
	char uri[1024];
	char *proxyname = NULL;
	char *proxy_port_s = NULL;
	int proxy_port = 0;
	int i, j;
	char *path = NULL;

	if (handle == NULL || cert == NULL || filename == NULL ||
	    retn_uri == NULL || format == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/* Get the proxy info */
	if (proxy != NULL) {
		proxyname = strtok(proxy, ":");
		proxy_port_s = strtok(NULL, "\0");
		if (proxy_port_s != NULL) {
			proxy_port = strtol(proxy_port_s, NULL, 0);
		} else {
			proxy_port = 8080; /* default */
		}
	}

	/*
	 * Get the CRL URI from the certificate's CRL Distribution
	 * Points extension and download the CRL file.  There maybe more than
	 * one CRL URI entries in the DP extension, so we will continue
	 * the process until a CRL file is sucessfully downloaded or we
	 * are running out the CRL URI's.
	 */
	ret = kmf_get_cert_crl_dist_pts((const KMF_DATA *)cert,
	    &crl_dps);
	if (ret != KMF_OK)
		goto out;

	for (i = 0; i < crl_dps.number; i++) {
		KMF_CRL_DIST_POINT *dp = &(crl_dps.dplist[i]);
		KMF_GENERALNAMES *fullname = &(dp->name.full_name);
		KMF_DATA *data;

		if (done)
			break;
		for (j = 0; j < fullname->number; j++) {
			data = &(fullname->namelist[j].name);
			(void) memcpy(uri, data->Data, data->Length);
			uri[data->Length] = '\0';
			ret = kmf_download_crl(handle, uri, proxyname,
			    proxy_port, 30, filename, format);
			if (ret == KMF_OK) {
				done = B_TRUE;
				path = malloc(data->Length + 1);
				if (path == NULL) {
					ret = KMF_ERR_MEMORY;
					goto out;
				}
				(void) strncpy(path, uri, data->Length);
				*retn_uri = path;
				break;
			}
		}
	}

out:
	kmf_free_crl_dist_pts(&crl_dps);
	return (ret);
}

static KMF_RETURN
check_crl_validity(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE kstype,
	char *crlfilename, KMF_DATA *issuer_cert)
{
	KMF_RETURN ret = KMF_OK;
	KMF_POLICY_RECORD *policy;

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	policy = handle->policy;

	/*
	 * NSS CRL is not file based, and its signature
	 * has been verified during CRL import.
	 * We only check CRL validity for file-based CRLs,
	 * NSS handles these checks internally.
	 */
	if (kstype == KMF_KEYSTORE_NSS)
		return (KMF_OK);

	/*
	 * Check the CRL signature if needed.
	 */
	if (!policy->validation_info.crl_info.ignore_crl_sign) {
		ret = kmf_verify_crl_file(handle, crlfilename,
		    issuer_cert);
		if (ret != KMF_OK)
			return (ret);
	}
	/*
	 * Check the CRL validity if needed.
	 */
	if (!policy->validation_info.crl_info.ignore_crl_date) {
		ret = kmf_check_crl_date(handle, crlfilename);
		if (ret != KMF_OK)
			return (ret);
	}

	return (ret);
}

static KMF_RETURN
cert_crl_check(KMF_HANDLE_T handle,  KMF_KEYSTORE_TYPE *kstype,
	KMF_DATA *user_cert, KMF_DATA *issuer_cert)
{
	KMF_POLICY_RECORD *policy;
	KMF_RETURN ret = KMF_OK;
	KMF_ATTRIBUTE attrlist[16];
	int numattr = 0;
	int fd;
	boolean_t crlchk;
	char user_certfile[MAXPATHLEN];
	char crlfile_tmp[MAXPATHLEN];
	char *basefilename = NULL;
	char *dir = NULL;
	char *crlfilename = NULL;
	char *proxy = NULL;
	char *uri = NULL;
	KMF_ENCODE_FORMAT format;

	if (handle == NULL || kstype == NULL || user_cert == NULL ||
	    issuer_cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (!is_valid_keystore_type(*kstype))
		return (KMF_ERR_BAD_PARAMETER);

	policy = handle->policy;

	/*
	 * If the get-crl-uri policy is TRUE, then download the CRL
	 * file first.   The newly downloaded file will be stored in the
	 * NSS internal database for NSS keystore, and stored in a file for
	 * the File-based CRL plugins (OpenSSL and PKCS11).
	 *
	 * For file-based plugins, if the get-crl-uri policy is FALSE,
	 * then the caller should provide a CRL file in the policy.
	 * Also, after this step is done, the "crlfilename" variable should
	 * contain the proper CRL file to be used for the rest of CRL
	 * validation process.
	 */
	basefilename = policy->validation_info.crl_info.basefilename;
	dir = policy->validation_info.crl_info.directory;
	if (policy->validation_info.crl_info.get_crl_uri) {
		/*
		 * Check to see if we already have this CRL.
		 */
		if (basefilename == NULL)
			basefilename = basename(uri);

		crlfilename = get_fullpath(dir == NULL ? "./" : dir,
		    basefilename);
		if (crlfilename == NULL) {
			ret = KMF_ERR_BAD_CRLFILE;
			goto cleanup;
		}

		/*
		 * If this file already exists and is valid, we don't need to
		 * download a new one.
		 */
		if ((fd = open(crlfilename, O_RDONLY)) != -1) {
			(void) close(fd);
			if ((ret = check_crl_validity(handle, *kstype,
			    crlfilename, issuer_cert)) == KMF_OK) {
				goto checkcrl;
			}
		}

		/*
		 * Create a temporary file to hold the new CRL file initially.
		 */
		(void) strlcpy(crlfile_tmp, CRLFILE_TEMPNAME,
		    sizeof (crlfile_tmp));
		if (mkstemp(crlfile_tmp) == -1) {
			ret = KMF_ERR_INTERNAL;
			goto cleanup;
		}

		/*
		 * Get the URI entry from the certificate's CRL distribution
		 * points extension and download the CRL file.
		 */
		proxy = policy->validation_info.crl_info.proxy;
		ret = cert_get_crl(handle, user_cert, proxy, crlfile_tmp,
		    &uri, &format);
		if (ret != KMF_OK) {
			(void) unlink(crlfile_tmp);
			goto cleanup;
		}
		/*
		 * If we just downloaded one, make sure it is OK.
		 */
		if ((ret = check_crl_validity(handle, *kstype, crlfile_tmp,
		    issuer_cert)) != KMF_OK)
			return (ret);

		/* Cache the CRL file. */
		if (*kstype == KMF_KEYSTORE_NSS) {
			/*
			 * For NSS keystore, import this CRL file into th
			 * internal database.
			 */
			numattr = 0;
			kmf_set_attr_at_index(attrlist, numattr,
			    KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (kstype));
			numattr++;

			kmf_set_attr_at_index(attrlist, numattr,
			    KMF_CRL_FILENAME_ATTR, crlfile_tmp,
			    strlen(crlfile_tmp));
			numattr++;

			crlchk = B_FALSE;
			kmf_set_attr_at_index(attrlist, numattr,
			    KMF_CRL_CHECK_ATTR,	&crlchk, sizeof (boolean_t));
			numattr++;

			ret = kmf_import_crl(handle, numattr, attrlist);
			(void) unlink(crlfile_tmp);
			if (ret != KMF_OK)
				goto cleanup;
		} else {
			if (rename(crlfile_tmp, crlfilename) == -1) {
				(void) unlink(crlfile_tmp);
				ret = KMF_ERR_WRITE_FILE;
				goto cleanup;
			}
		}
	} else {
		/*
		 * If the get_crl_uri policy is FALSE, for File-based CRL
		 * plugins, get the input CRL file from the policy.
		 */
		if (*kstype != KMF_KEYSTORE_NSS) {
			if (basefilename == NULL) {
				ret = KMF_ERR_BAD_PARAMETER;
				goto cleanup;
			}

			crlfilename = get_fullpath(dir == NULL ? "./" : dir,
			    basefilename);
			if (crlfilename == NULL) {
				ret = KMF_ERR_BAD_CRLFILE;
				goto cleanup;
			}
			/*
			 * Make sure this CRL is still valid.
			 */
			if ((ret = check_crl_validity(handle, *kstype,
			    crlfilename, issuer_cert)) != KMF_OK)
				return (ret);
			}
	}

checkcrl:
	/*
	 * Check the CRL revocation for the certificate.
	 */
	numattr = 0;

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR,
	    kstype, sizeof (kstype));
	numattr++;

	switch (*kstype) {
	case KMF_KEYSTORE_NSS:
		kmf_set_attr_at_index(attrlist, numattr,
		    KMF_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA));
		numattr++;
		break;
	case KMF_KEYSTORE_PK11TOKEN:
	case KMF_KEYSTORE_OPENSSL:
		/*
		 * Create temporary file to hold the user certificate.
		 */
		(void) strlcpy(user_certfile, CERTFILE_TEMPNAME,
		    sizeof (user_certfile));
		if (mkstemp(user_certfile) == -1) {
			ret = KMF_ERR_INTERNAL;
			goto cleanup;
		}

		ret = kmf_create_cert_file(user_cert, KMF_FORMAT_ASN1,
		    user_certfile);
		if (ret != KMF_OK)  {
			goto cleanup;
		}

		kmf_set_attr_at_index(attrlist,  numattr,
		    KMF_CERT_FILENAME_ATTR,
		    user_certfile, strlen(user_certfile));
		numattr++;

		kmf_set_attr_at_index(attrlist,  numattr,
		    KMF_CRL_FILENAME_ATTR,
		    crlfilename, strlen(crlfilename));
		numattr++;
		break;
	default:
		ret = KMF_ERR_PLUGIN_NOTFOUND;
		goto cleanup;
	}

	ret = kmf_find_cert_in_crl(handle, numattr, attrlist);
	if (ret == KMF_ERR_NOT_REVOKED)  {
		ret = KMF_OK;
	}

cleanup:
	(void) unlink(user_certfile);

	if (crlfilename != NULL)
		free(crlfilename);

	if (uri != NULL)
		free(uri);

	return (ret);
}

static KMF_RETURN
cert_ocsp_check(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
	KMF_DATA *user_cert, KMF_DATA *issuer_cert, KMF_DATA *response,
	char *slotlabel, char *dirpath)
{
	KMF_RETURN ret = KMF_OK;
	KMF_POLICY_RECORD *policy;
	KMF_DATA *new_response = NULL;
	boolean_t ignore_response_sign = B_FALSE;
	uint32_t ltime = 0;
	KMF_DATA *signer_cert = NULL;
	KMF_BIGINT sernum = { NULL, 0 };
	int response_status;
	int reason;
	int cert_status;
	KMF_ATTRIBUTE attrlist[32];
	int numattr;

	if (handle == NULL || kstype == NULL || user_cert == NULL ||
	    issuer_cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	policy = handle->policy;

	/*
	 * Get the response lifetime from policy.
	 */
	if (policy->VAL_OCSP_BASIC.response_lifetime != NULL &&
	    (str2lifetime(policy->VAL_OCSP_BASIC.response_lifetime, &ltime)
	    < 0))
		return (KMF_ERR_OCSP_RESPONSE_LIFETIME);

	/*
	 * Get the ignore_response_sign policy.
	 *
	 * If ignore_response_sign is FALSE, we need to verify the response.
	 * Find the OCSP Responder certificate if it is specified in the OCSP
	 * policy.
	 */
	ignore_response_sign = policy->VAL_OCSP_BASIC.ignore_response_sign;

	if (ignore_response_sign == B_FALSE &&
	    policy->VAL_OCSP.has_resp_cert == B_TRUE) {
		char *signer_name;
		KMF_X509_DER_CERT signer_retrcert;
		uchar_t *bytes = NULL;
		size_t bytelen;
		uint32_t num = 0;
		KMF_ATTRIBUTE fc_attrlist[16];
		int fc_numattr = 0;
		char *dir = "./";

		if (policy->VAL_OCSP_RESP_CERT.name == NULL ||
		    policy->VAL_OCSP_RESP_CERT.serial == NULL)
			return (KMF_ERR_POLICY_NOT_FOUND);

		signer_cert = malloc(sizeof (KMF_DATA));
		if (signer_cert == NULL) {
			ret = KMF_ERR_MEMORY;
			goto out;
		}
		(void) memset(signer_cert, 0, sizeof (KMF_DATA));

		signer_name = policy->VAL_OCSP_RESP_CERT.name;
		ret = kmf_hexstr_to_bytes(
		    (uchar_t *)policy->VAL_OCSP_RESP_CERT.serial,
		    &bytes, &bytelen);
		if (ret != KMF_OK || bytes == NULL) {
			ret = KMF_ERR_OCSP_POLICY;
			goto out;
		}
		sernum.val = bytes;
		sernum.len = bytelen;

		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_KEYSTORE_TYPE_ATTR, kstype,
		    sizeof (KMF_KEYSTORE_TYPE));
		fc_numattr++;

		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_SUBJECT_NAME_ATTR, signer_name, strlen(signer_name));
		fc_numattr++;

		kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_BIGINT_ATTR,
		    &sernum, sizeof (KMF_BIGINT));
		fc_numattr++;

		if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) {
			kmf_set_attr_at_index(fc_attrlist, fc_numattr,
			    KMF_TOKEN_LABEL_ATTR, slotlabel,
			    strlen(slotlabel));
			fc_numattr++;
		}

		if (*kstype == KMF_KEYSTORE_OPENSSL) {
			if (dirpath == NULL) {
				kmf_set_attr_at_index(fc_attrlist, fc_numattr,
				    KMF_DIRPATH_ATTR, dir, strlen(dir));
				fc_numattr++;
			} else {
				kmf_set_attr_at_index(fc_attrlist, fc_numattr,
				    KMF_DIRPATH_ATTR, dirpath,
				    strlen(dirpath));
				fc_numattr++;
			}
		}

		num = 0;
		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_COUNT_ATTR, &num, sizeof (uint32_t));
		fc_numattr++;

		ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
		if (ret != KMF_OK || num != 1) {
			if (num == 0)
				ret = KMF_ERR_CERT_NOT_FOUND;
			if (num > 0)
				ret = KMF_ERR_CERT_MULTIPLE_FOUND;
			goto out;
		}

		(void) memset(&signer_retrcert, 0, sizeof (KMF_X509_DER_CERT));
		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_X509_DER_CERT_ATTR, &signer_retrcert,
		    sizeof (KMF_X509_DER_CERT));
		fc_numattr++;

		ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
		if (ret == KMF_OK) {
			signer_cert->Length =
			    signer_retrcert.certificate.Length;
			signer_cert->Data = signer_retrcert.certificate.Data;
		} else {
			goto out;
		}
	}

	/*
	 * If the caller provides an OCSP response, we will use it directly.
	 * Otherwise, we will try to fetch an OCSP response for the given
	 * certificate now.
	 */
	if (response == NULL) {
		new_response = (KMF_DATA *) malloc(sizeof (KMF_DATA));
		if (new_response == NULL) {
			ret = KMF_ERR_MEMORY;
			goto out;
		}
		new_response->Data = NULL;
		new_response->Length = 0;

		ret = kmf_get_ocsp_for_cert(handle, user_cert, issuer_cert,
		    new_response);
		if (ret != KMF_OK)
			goto out;
	}

	/*
	 * Process the OCSP response and retrieve the certificate status.
	 */
	numattr = 0;
	kmf_set_attr_at_index(attrlist, numattr, KMF_ISSUER_CERT_DATA_ATTR,
	    issuer_cert, sizeof (KMF_DATA));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_USER_CERT_DATA_ATTR,
	    user_cert, sizeof (KMF_DATA));
	numattr++;

	if (signer_cert != NULL) {
		kmf_set_attr_at_index(attrlist, numattr,
		    KMF_SIGNER_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA));
		numattr++;
	}

	kmf_set_attr_at_index(attrlist, numattr, KMF_OCSP_RESPONSE_DATA_ATTR,
	    response == NULL ? new_response : response, sizeof (KMF_DATA));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_RESPONSE_LIFETIME_ATTR,
	    &ltime, sizeof (uint32_t));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr,
	    KMF_IGNORE_RESPONSE_SIGN_ATTR, &ignore_response_sign,
	    sizeof (boolean_t));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr,
	    KMF_OCSP_RESPONSE_STATUS_ATTR, &response_status, sizeof (int));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr,
	    KMF_OCSP_RESPONSE_REASON_ATTR, &reason, sizeof (int));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr,
	    KMF_OCSP_RESPONSE_CERT_STATUS_ATTR, &cert_status, sizeof (int));
	numattr++;

	ret = kmf_get_ocsp_status_for_cert(handle, numattr, attrlist);
	if (ret == KMF_OK) {
		switch (cert_status) {
		case OCSP_GOOD:
			break;
		case OCSP_UNKNOWN:
			ret = KMF_ERR_OCSP_UNKNOWN_CERT;
			break;
		case OCSP_REVOKED:
			ret = KMF_ERR_OCSP_REVOKED;
			break;
		}
	}

out:
	if (new_response) {
		kmf_free_data(new_response);
		free(new_response);
	}

	if (signer_cert) {
		kmf_free_data(signer_cert);
		free(signer_cert);
	}

	if (sernum.val != NULL)
		free(sernum.val);

	return (ret);
}

static KMF_RETURN
cert_ku_check(KMF_HANDLE_T handle, KMF_DATA *cert)
{
	KMF_POLICY_RECORD *policy;
	KMF_X509EXT_KEY_USAGE keyusage;
	KMF_RETURN ret = KMF_OK;
	KMF_X509EXT_BASICCONSTRAINTS constraint;
	KMF_BOOL	critical = B_FALSE;

	if (handle == NULL || cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	policy = handle->policy;
	(void) memset(&keyusage, 0, sizeof (keyusage));
	ret = kmf_get_cert_ku(cert, &keyusage);

	if (ret == KMF_ERR_EXTENSION_NOT_FOUND) {
		if (policy->ku_bits) {
			/* keyusage is not set in cert but is set in policy */
			return (KMF_ERR_KEYUSAGE);
		} else {
			/* no keyusage set in both cert and policy */
			return (KMF_OK);
		}
	}

	if (ret != KMF_OK) {
		/* real error */
		return (ret);
	}

	/*
	 * If KeyCertSign is set, then constraints.cA must be TRUE and
	 * marked critical.
	 */
	if ((keyusage.KeyUsageBits & KMF_keyCertSign)) {
		(void) memset(&constraint, 0, sizeof (constraint));
		ret = kmf_get_cert_basic_constraint(cert,
		    &critical, &constraint);

		if (ret != KMF_OK) {
			/* real error */
			return (ret);
		}
		if (!constraint.cA || !critical)
			return (KMF_ERR_KEYUSAGE);
	}

	/*
	 * Rule: if the KU bit is set in policy, the corresponding KU bit
	 * must be set in the certificate (but not vice versa).
	 */
	if ((policy->ku_bits & keyusage.KeyUsageBits) == policy->ku_bits) {
		return (KMF_OK);
	} else {
		return (KMF_ERR_KEYUSAGE);
	}

}

static KMF_RETURN
cert_eku_check(KMF_HANDLE_T handle, KMF_DATA *cert)
{
	KMF_POLICY_RECORD *policy;
	KMF_RETURN ret = KMF_OK;
	KMF_X509EXT_EKU eku;
	uint16_t cert_eku = 0, policy_eku = 0;
	int i;

	if (handle == NULL || cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	policy = handle->policy;

	/*
	 * If the policy does not have any EKU, then there is
	 * nothing further to check.
	 */
	if (policy->eku_set.eku_count == 0)
		return (KMF_OK);

	ret = kmf_get_cert_eku(cert, &eku);
	if ((ret != KMF_ERR_EXTENSION_NOT_FOUND) && (ret != KMF_OK)) {
		/* real error */
		return (ret);
	}

	if (ret == KMF_ERR_EXTENSION_NOT_FOUND) {
		cert_eku = 0;
	} else {
		/*
		 * Build the EKU bitmap based on the certificate
		 */
		for (i = 0; i < eku.nEKUs; i++) {
			if (IsEqualOid(&eku.keyPurposeIdList[i],
			    (KMF_OID *)&KMFOID_PKIX_KP_ServerAuth)) {
				cert_eku |= KMF_EKU_SERVERAUTH;
			} else if (IsEqualOid(&eku.keyPurposeIdList[i],
			    (KMF_OID *)&KMFOID_PKIX_KP_ClientAuth)) {
				cert_eku |= KMF_EKU_CLIENTAUTH;
			} else if (IsEqualOid(&eku.keyPurposeIdList[i],
			    (KMF_OID *)&KMFOID_PKIX_KP_CodeSigning)) {
				cert_eku |= KMF_EKU_CODESIGNING;
			} else if (IsEqualOid(&eku.keyPurposeIdList[i],
			    (KMF_OID *)&KMFOID_PKIX_KP_EmailProtection)) {
				cert_eku |= KMF_EKU_EMAIL;
			} else if (IsEqualOid(&eku.keyPurposeIdList[i],
			    (KMF_OID *)&KMFOID_PKIX_KP_TimeStamping)) {
				cert_eku |= KMF_EKU_TIMESTAMP;
			} else if (IsEqualOid(&eku.keyPurposeIdList[i],
			    (KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning)) {
				cert_eku |= KMF_EKU_OCSPSIGNING;
			} else if (!policy->ignore_unknown_ekus) {
				return (KMF_ERR_KEYUSAGE);
			}
		} /* for */
	}


	/*
	 * Build the EKU bitmap based on the policy
	 */
	for (i = 0; i < policy->eku_set.eku_count; i++) {
		if (IsEqualOid(&policy->eku_set.ekulist[i],
		    (KMF_OID *)&KMFOID_PKIX_KP_ServerAuth)) {
			policy_eku |= KMF_EKU_SERVERAUTH;
		} else if (IsEqualOid(&policy->eku_set.ekulist[i],
		    (KMF_OID *)&KMFOID_PKIX_KP_ClientAuth)) {
			policy_eku |= KMF_EKU_CLIENTAUTH;
		} else if (IsEqualOid(&policy->eku_set.ekulist[i],
		    (KMF_OID *)&KMFOID_PKIX_KP_CodeSigning)) {
			policy_eku |= KMF_EKU_CODESIGNING;
		} else if (IsEqualOid(&policy->eku_set.ekulist[i],
		    (KMF_OID *)&KMFOID_PKIX_KP_EmailProtection)) {
			policy_eku |= KMF_EKU_EMAIL;
		} else if (IsEqualOid(&policy->eku_set.ekulist[i],
		    (KMF_OID *)&KMFOID_PKIX_KP_TimeStamping)) {
			policy_eku |= KMF_EKU_TIMESTAMP;
		} else if (IsEqualOid(&policy->eku_set.ekulist[i],
		    (KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning)) {
			policy_eku |= KMF_EKU_OCSPSIGNING;
		} else if (!policy->ignore_unknown_ekus) {
			return (KMF_ERR_KEYUSAGE);
		}
	} /* for */

	/*
	 * Rule: if the EKU OID is set in policy, the corresponding EKU OID
	 * must be set in the certificate (but not vice versa).
	 */
	if ((policy_eku & cert_eku) == policy_eku) {
		return (KMF_OK);
	} else {
		return (KMF_ERR_KEYUSAGE);
	}
}

static KMF_RETURN
find_issuer_cert(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
    char *user_issuer, KMF_DATA *issuer_cert,
    char *slotlabel, char *dirpath)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_DER_CERT *certlist = NULL;
	uint32_t i, num = 0;
	time_t t_notbefore;
	time_t t_notafter;
	time_t latest;
	KMF_DATA tmp_cert = {0, NULL};
	KMF_ATTRIBUTE fc_attrlist[16];
	int fc_numattr = 0;
	char *dir = "./";

	if (handle == NULL || kstype == NULL || user_issuer == NULL ||
	    issuer_cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (!is_valid_keystore_type(*kstype))
		return (KMF_ERR_BAD_PARAMETER);

	kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_KEYSTORE_TYPE_ATTR,
	    kstype, sizeof (KMF_KEYSTORE_TYPE));
	fc_numattr++;

	kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_SUBJECT_NAME_ATTR,
	    user_issuer, strlen(user_issuer));
	fc_numattr++;

	if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) {
		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel));
		fc_numattr++;
	}

	if (*kstype == KMF_KEYSTORE_OPENSSL) {
		if (dirpath == NULL) {
			kmf_set_attr_at_index(fc_attrlist, fc_numattr,
			    KMF_DIRPATH_ATTR, dir, strlen(dir));
			fc_numattr++;
		} else {
			kmf_set_attr_at_index(fc_attrlist, fc_numattr,
			    KMF_DIRPATH_ATTR, dirpath, strlen(dirpath));
			fc_numattr++;
		}
	}

	num = 0;
	kmf_set_attr_at_index(fc_attrlist, fc_numattr,
	    KMF_COUNT_ATTR, &num, sizeof (uint32_t));
	fc_numattr++;

	ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);

	if (ret == KMF_OK && num > 0) {
		certlist = (KMF_X509_DER_CERT *)malloc(num *
		    sizeof (KMF_X509_DER_CERT));

		if (certlist == NULL) {
			ret = KMF_ERR_MEMORY;
			goto out;
		}

		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_X509_DER_CERT_ATTR, certlist,
		    sizeof (KMF_X509_DER_CERT));
		fc_numattr++;

		ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
		if (ret != KMF_OK) {
			free(certlist);
			certlist = NULL;
			goto out;
		}
	} else {
		goto out;
	}

	if (num == 1) {
		/* only one issuer cert is found */
		tmp_cert.Length = certlist[0].certificate.Length;
		tmp_cert.Data = certlist[0].certificate.Data;
	} else {
		/*
		 * More than one issuer certs are found. We will
		 * pick the latest one.
		 */
		latest = 0;
		for (i = 0; i < num; i++) {
			ret = kmf_get_cert_validity(&certlist[i].certificate,
			    &t_notbefore, &t_notafter);
			if (ret != KMF_OK) {
				ret = KMF_ERR_VALIDITY_PERIOD;
				goto out;
			}

			if (t_notbefore > latest) {
				tmp_cert.Length =
				    certlist[i].certificate.Length;
				tmp_cert.Data =
				    certlist[i].certificate.Data;
				latest = t_notbefore;
			}

		}
	}

	issuer_cert->Length = tmp_cert.Length;
	issuer_cert->Data = malloc(tmp_cert.Length);
	if (issuer_cert->Data == NULL) {
		ret = KMF_ERR_MEMORY;
		goto out;
	}
	(void) memcpy(issuer_cert->Data, tmp_cert.Data,
	    tmp_cert.Length);

out:
	if (certlist != NULL) {
		for (i = 0; i < num; i++)
			kmf_free_kmf_cert(handle, &certlist[i]);
		free(certlist);
	}

	return (ret);

}

static KMF_RETURN
find_ta_cert(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
	KMF_DATA *ta_cert, KMF_X509_NAME *user_issuerDN,
	char *slotlabel, char *dirpath)
{
	KMF_POLICY_RECORD *policy;
	KMF_RETURN ret = KMF_OK;
	uint32_t num = 0;
	char *ta_name;
	KMF_BIGINT serial = { NULL, 0 };
	uchar_t *bytes = NULL;
	size_t bytelen;
	KMF_X509_DER_CERT ta_retrCert;
	char *ta_subject = NULL;
	KMF_X509_NAME ta_subjectDN;
	KMF_ATTRIBUTE fc_attrlist[16];
	int fc_numattr = 0;
	char *dir = "./";

	if (handle == NULL || kstype == NULL || ta_cert == NULL ||
	    user_issuerDN == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (!is_valid_keystore_type(*kstype))
		return (KMF_ERR_BAD_PARAMETER);

	/* Get the TA name and serial number from the policy */
	policy = handle->policy;
	ta_name = policy->ta_name;
	ret = kmf_hexstr_to_bytes((uchar_t *)policy->ta_serial,
	    &bytes, &bytelen);
	if (ret != KMF_OK || bytes == NULL) {
		ret = KMF_ERR_TA_POLICY;
		goto out;
	}
	serial.val = bytes;
	serial.len = bytelen;

	/* set up fc_attrlist for kmf_find_cert */
	kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_KEYSTORE_TYPE_ATTR,
	    kstype, sizeof (KMF_KEYSTORE_TYPE));
	fc_numattr++;

	kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_SUBJECT_NAME_ATTR,
	    ta_name, strlen(ta_name));
	fc_numattr++;

	kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_BIGINT_ATTR,
	    &serial, sizeof (KMF_BIGINT));
	fc_numattr++;

	if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) {
		kmf_set_attr_at_index(fc_attrlist, fc_numattr,
		    KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel));
		fc_numattr++;
	}

	if (*kstype == KMF_KEYSTORE_OPENSSL) {
		if (dirpath == NULL) {
			kmf_set_attr_at_index(fc_attrlist, fc_numattr,
			    KMF_DIRPATH_ATTR, dir, strlen(dir));
			fc_numattr++;
		} else {
			kmf_set_attr_at_index(fc_attrlist, fc_numattr,
			    KMF_DIRPATH_ATTR, dirpath, strlen(dirpath));
			fc_numattr++;
		}
	}

	num = 0;
	kmf_set_attr_at_index(fc_attrlist, fc_numattr,
	    KMF_COUNT_ATTR, &num, sizeof (uint32_t));
	fc_numattr++;

	ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
	if (ret != KMF_OK || num != 1)  {
		if (num == 0)
			ret = KMF_ERR_CERT_NOT_FOUND;
		if (num > 1)
			ret = KMF_ERR_CERT_MULTIPLE_FOUND;
		goto out;
	}

	kmf_set_attr_at_index(fc_attrlist, fc_numattr,
	    KMF_X509_DER_CERT_ATTR, &ta_retrCert, sizeof (KMF_X509_DER_CERT));
	fc_numattr++;

	ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
	if (ret == KMF_OK)  {
		ta_cert->Length = ta_retrCert.certificate.Length;
		ta_cert->Data = malloc(ta_retrCert.certificate.Length);
		if (ta_cert->Data == NULL) {
			ret = KMF_ERR_MEMORY;
			goto out;
		}
		(void) memcpy(ta_cert->Data, ta_retrCert.certificate.Data,
		    ta_retrCert.certificate.Length);
	} else {
		goto out;
	}

	/*
	 * The found TA's name must be matching with issuer name in
	 * subscriber's certificate.
	 */
	(void) memset(&ta_subjectDN, 0, sizeof (ta_subjectDN));

	ret = kmf_get_cert_subject_str(handle, ta_cert, &ta_subject);
	if (ret != KMF_OK)
		goto out;

	ret = kmf_dn_parser(ta_subject,  &ta_subjectDN);
	if (ret != KMF_OK)
		goto out;

	if (kmf_compare_rdns(user_issuerDN, &ta_subjectDN) != 0)
		ret = KMF_ERR_CERT_NOT_FOUND;

	kmf_free_dn(&ta_subjectDN);

	/* Make sure the TA cert has the correct extensions */
	if (ret == KMF_OK) {
		ret = check_key_usage(handle, ta_cert, KMF_KU_SIGN_CERT);
		if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
			ret = KMF_OK;
	}
out:
	if (ta_retrCert.certificate.Data)
		kmf_free_kmf_cert(handle, &ta_retrCert);

	if ((ret != KMF_OK) && (ta_cert->Data != NULL))
		free(ta_cert->Data);

	if (serial.val != NULL)
		free(serial.val);

	if (ta_subject)
		free(ta_subject);

	return (ret);
}

KMF_RETURN
kmf_validate_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE *kstype = NULL;
	KMF_DATA *pcert = NULL;
	int *result = NULL;
	char *slotlabel = NULL;
	char *dirpath = NULL;
	KMF_DATA *ocsp_response = NULL;
	KMF_DATA ta_cert = {0, NULL};
	KMF_DATA issuer_cert = {0, NULL};
	char *user_issuer = NULL, *user_subject = NULL;
	KMF_X509_NAME user_issuerDN, user_subjectDN;
	boolean_t	self_signed = B_FALSE;
	KMF_POLICY_RECORD *policy;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
	    {KMF_VALIDATE_RESULT_ATTR, FALSE, 1, sizeof (int)}
	};
	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs,
	    0, NULL, numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	policy = handle->policy;

	/* Get the attribute values */
	kstype = kmf_get_attr_ptr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr);
	pcert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr);
	result = kmf_get_attr_ptr(KMF_VALIDATE_RESULT_ATTR, attrlist, numattr);
	if (kstype == NULL || pcert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	slotlabel = kmf_get_attr_ptr(KMF_TOKEN_LABEL_ATTR, attrlist, numattr);
	dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr);
	ocsp_response = kmf_get_attr_ptr(KMF_OCSP_RESPONSE_DATA_ATTR, attrlist,
	    numattr);

	/* Initialize the returned result */
	*result = KMF_CERT_VALIDATE_OK;

	/*
	 * Get the issuer information from the input certficate first.
	 */
	if ((ret = kmf_get_cert_issuer_str(handle, pcert,
	    &user_issuer)) != KMF_OK) {
		*result |= KMF_CERT_VALIDATE_ERR_USER;
		goto out;
	}

	if ((ret = kmf_dn_parser(user_issuer,  &user_issuerDN)) != KMF_OK) {
		*result |= KMF_CERT_VALIDATE_ERR_USER;
		goto out;
	}

	/*
	 * Check if the certificate is a self-signed cert.
	 */
	if ((ret = kmf_get_cert_subject_str(handle, pcert,
	    &user_subject)) != KMF_OK) {
		*result |= KMF_CERT_VALIDATE_ERR_USER;
		kmf_free_dn(&user_issuerDN);
		goto out;
	}

	if ((ret = kmf_dn_parser(user_subject,  &user_subjectDN)) != KMF_OK) {
		*result |= KMF_CERT_VALIDATE_ERR_USER;
		kmf_free_dn(&user_issuerDN);
		goto out;
	}

	if ((kmf_compare_rdns(&user_issuerDN, &user_subjectDN)) == 0) {
		/*
		 * this is a self-signed cert
		 */
		self_signed = B_TRUE;
	}

	kmf_free_dn(&user_subjectDN);

	/*
	 * Check KeyUsage extension of the subscriber's certificate
	 */
	ret = cert_ku_check(handle, pcert);
	if (ret != KMF_OK)  {
		*result |= KMF_CERT_VALIDATE_ERR_KEYUSAGE;
		goto out;
	}

	/*
	 * Validate Extended KeyUsage extension
	 */
	ret = cert_eku_check(handle, pcert);
	if (ret != KMF_OK)  {
		*result |= KMF_CERT_VALIDATE_ERR_EXT_KEYUSAGE;
		goto out;
	}

	/*
	 * Check the certificate's validity period
	 *
	 * This step is needed when "ignore_date" in policy is set
	 * to false.
	 */
	if (!policy->ignore_date) {
		/*
		 * Validate expiration date
		 */
		ret = kmf_check_cert_date(handle, pcert);
		if (ret != KMF_OK)  {
			*result |= KMF_CERT_VALIDATE_ERR_TIME;
			goto out;
		}
	}

	/*
	 * When "ignore_trust_anchor" in policy is set to FALSE,
	 * we will try to find the TA cert based on the TA policy
	 * attributes.
	 *
	 * TA's subject name (ta_name) and serial number (ta_serial)
	 * are defined as optional attributes in policy dtd, but they
	 * should exist in policy when "ignore_trust_anchor" is set
	 * to FALSE. The policy verification code has enforced that.
	 */
	if (policy->ignore_trust_anchor) {
		goto check_revocation;
	}

	/*
	 * Verify the signature of subscriber's certificate using
	 * TA certificate.
	 */
	if (self_signed) {
		ret = verify_cert_with_cert(handle, pcert, pcert);
	} else {
		ret = find_ta_cert(handle, kstype, &ta_cert,
		    &user_issuerDN, slotlabel, dirpath);
		if (ret != KMF_OK)  {
			*result |= KMF_CERT_VALIDATE_ERR_TA;
			goto out;
		}

		ret = verify_cert_with_cert(handle, pcert, &ta_cert);
	}
	if (ret != KMF_OK)  {
		*result |= KMF_CERT_VALIDATE_ERR_SIGNATURE;
		goto out;
	}

check_revocation:
	/*
	 * Check certificate revocation
	 */
	if (self_signed) {
		/* skip revocation checking */
		goto out;
	}

	/*
	 * When CRL or OCSP revocation method is set in the policy,
	 * we will try to find the issuer of the subscriber certificate
	 * using the issuer name of the subscriber certificate. The
	 * issuer certificate will be used to do the CRL checking
	 * and OCSP checking.
	 */
	if (!(policy->revocation & KMF_REVOCATION_METHOD_CRL) &&
	    !(policy->revocation & KMF_REVOCATION_METHOD_OCSP)) {
		goto out;
	}

	ret = find_issuer_cert(handle, kstype, user_issuer, &issuer_cert,
	    slotlabel, dirpath);
	if (ret != KMF_OK)  {
		*result |= KMF_CERT_VALIDATE_ERR_ISSUER;
		goto out;
	}

	if (policy->revocation & KMF_REVOCATION_METHOD_CRL) {
		ret = cert_crl_check(handle, kstype, pcert, &issuer_cert);
		if (ret != KMF_OK)  {
			*result |= KMF_CERT_VALIDATE_ERR_CRL;
			goto out;
		}
	}

	if (policy->revocation & KMF_REVOCATION_METHOD_OCSP) {
		ret = cert_ocsp_check(handle, kstype, pcert, &issuer_cert,
		    ocsp_response, slotlabel, dirpath);
		if (ret != KMF_OK)  {
			*result |= KMF_CERT_VALIDATE_ERR_OCSP;
			goto out;
		}
	}

out:
	if (user_issuer) {
		kmf_free_dn(&user_issuerDN);
		free(user_issuer);
	}

	if (user_subject)
		free(user_subject);

	if (ta_cert.Data)
		free(ta_cert.Data);

	if (issuer_cert.Data)
		free(issuer_cert.Data);

	return (ret);

}

KMF_RETURN
kmf_create_cert_file(const KMF_DATA *certdata, KMF_ENCODE_FORMAT format,
	char *certfile)
{
	KMF_RETURN rv = KMF_OK;
	int fd = -1;
	KMF_DATA pemdata = {NULL, 0};

	if (certdata == NULL || certfile == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (format != KMF_FORMAT_PEM && format != KMF_FORMAT_ASN1)
		return (KMF_ERR_BAD_PARAMETER);

	if (format == KMF_FORMAT_PEM) {
		int len;
		rv = kmf_der_to_pem(KMF_CERT,
		    certdata->Data, certdata->Length,
		    &pemdata.Data, &len);
		if (rv != KMF_OK)
			goto cleanup;
		pemdata.Length = (size_t)len;
	}

	if ((fd = open(certfile, O_CREAT | O_RDWR | O_TRUNC, 0644)) == -1) {
		rv = KMF_ERR_OPEN_FILE;
		goto cleanup;
	}

	if (format == KMF_FORMAT_PEM) {
		if (write(fd, pemdata.Data, pemdata.Length) !=
		    pemdata.Length) {
			rv = KMF_ERR_WRITE_FILE;
		}
	} else {
		if (write(fd, certdata->Data, certdata->Length) !=
		    certdata->Length) {
			rv = KMF_ERR_WRITE_FILE;
		}
	}

cleanup:
	if (fd != -1)
		(void) close(fd);

	kmf_free_data(&pemdata);

	return (rv);
}

/*
 * kmf_is_cert_data
 *
 * Determine if a KMF_DATA buffer contains an encoded X.509 certificate.
 *
 * Return:
 *   KMF_OK if it is a certificate
 *   KMF_ERR_ENCODING (or other error) if not.
 */
KMF_RETURN
kmf_is_cert_data(KMF_DATA *data, KMF_ENCODE_FORMAT *fmt)
{
	KMF_RETURN rv = KMF_OK;
	KMF_X509_CERTIFICATE *x509 = NULL;
	KMF_DATA oldpem = {0, NULL};
	uchar_t *d = NULL;
	int len = 0;

	if (data == NULL || fmt == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	rv = kmf_get_data_format(data, fmt);
	if (rv != KMF_OK)
		return (rv);
	switch (*fmt) {
		case KMF_FORMAT_ASN1:
			rv = DerDecodeSignedCertificate(data, &x509);
			break;
		case KMF_FORMAT_PEM:
			/* Convert to ASN.1 DER first */
			rv = kmf_pem_to_der(data->Data, data->Length,
			    &d, &len);
			if (rv != KMF_OK)
				return (rv);
			oldpem.Data = d;
			oldpem.Length = len;
			rv = DerDecodeSignedCertificate(&oldpem, &x509);
			kmf_free_data(&oldpem);
			break;
		case KMF_FORMAT_PKCS12:
		case KMF_FORMAT_UNDEF:
		default:
			return (KMF_ERR_ENCODING);
	}

	if (x509 != NULL) {
		kmf_free_signed_cert(x509);
		free(x509);
	}
	return (rv);
}

KMF_RETURN
kmf_is_cert_file(KMF_HANDLE_T handle, char *filename,
	KMF_ENCODE_FORMAT *pformat)
{
	KMF_RETURN ret;
	KMF_DATA filedata;

	CLEAR_ERROR(handle, ret);
	if (ret != KMF_OK)
		return (ret);

	if (filename  == NULL || pformat == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	ret = kmf_read_input_file(handle, filename, &filedata);
	if (ret != KMF_OK)
		return (ret);

	ret = kmf_is_cert_data(&filedata, pformat);
	if (ret == KMF_ERR_BAD_CERT_FORMAT)
		ret = KMF_ERR_BAD_CERTFILE;

	kmf_free_data(&filedata);
	return (ret);
}

/*
 * This function checks the validity period of a der-encoded certificate.
 */
KMF_RETURN
kmf_check_cert_date(KMF_HANDLE_T handle, const KMF_DATA *cert)
{
	KMF_RETURN rv;
	struct tm *gmt;
	time_t t_now;
	time_t t_notbefore;
	time_t t_notafter;
	KMF_POLICY_RECORD *policy;
	uint32_t adj;

	CLEAR_ERROR(handle, rv);
	if (rv != KMF_OK)
		return (rv);

	if (cert == NULL || cert->Data == NULL || cert->Length == 0)
		return (KMF_ERR_BAD_PARAMETER);

	policy = handle->policy;
	rv = kmf_get_cert_validity(cert, &t_notbefore, &t_notafter);
	if (rv != KMF_OK)
		return (rv);

	/*
	 * Get the current time. The time returned from time() is local which
	 * cannot be used directly. It must be converted to UTC/GMT first.
	 */
	t_now = time(NULL);
	gmt = gmtime(&t_now);
	t_now = mktime(gmt);

	/*
	 * Adjust the validity time
	 */
	if (policy->validity_adjusttime != NULL) {
		if (str2lifetime(policy->validity_adjusttime, &adj) < 0)
			return (KMF_ERR_VALIDITY_PERIOD);
	} else {
		adj = 0;
	}

	t_notafter += adj;
	t_notbefore -= adj;

	if (t_now <= t_notafter && t_now >= t_notbefore) {
		rv = KMF_OK;
	} else {
		rv = KMF_ERR_VALIDITY_PERIOD;
	}

	return (rv);
}

KMF_RETURN
kmf_export_pk12(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN ret = KMF_OK;
	KMF_KEYSTORE_TYPE kstype;

	KMF_ATTRIBUTE_TESTER required_attrs[] = {
	    {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
	    {KMF_OUTPUT_FILENAME_ATTR, TRUE, 1, 0},
	};

	int num_req_attrs = sizeof (required_attrs) /
	    sizeof (KMF_ATTRIBUTE_TESTER);

	if (handle == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	CLEAR_ERROR(handle, ret);

	ret = test_attributes(num_req_attrs, required_attrs, 0, NULL,
	    numattr, attrlist);
	if (ret != KMF_OK)
		return (ret);

	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
	    &kstype, NULL);
	if (ret != KMF_OK)
		return (ret);

	plugin = FindPlugin(handle, kstype);
	if (plugin == NULL || plugin->funclist->ExportPK12 == NULL)
		return (KMF_ERR_PLUGIN_NOTFOUND);

	return (plugin->funclist->ExportPK12(handle, numattr, attrlist));
}


KMF_RETURN
kmf_build_pk12(KMF_HANDLE_T handle, int numcerts,
    KMF_X509_DER_CERT *certlist, int numkeys, KMF_KEY_HANDLE *keylist,
    KMF_CREDENTIAL *p12cred, char *filename)
{
	KMF_RETURN rv;
	KMF_PLUGIN *plugin;
	KMF_RETURN (*buildpk12)(KMF_HANDLE *, int, KMF_X509_DER_CERT *,
	    int, KMF_KEY_HANDLE *, KMF_CREDENTIAL *, char *);

	CLEAR_ERROR(handle, rv);
	if (rv != KMF_OK)
		return (rv);

	if (filename == NULL ||	p12cred == NULL ||
	    (certlist == NULL && keylist == NULL))
		return (KMF_ERR_BAD_PARAMETER);

	plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL);
	if (plugin == NULL || plugin->dldesc == NULL) {
		return (KMF_ERR_PLUGIN_NOTFOUND);
	}

	buildpk12 = (KMF_RETURN(*)())dlsym(plugin->dldesc,
	    "openssl_build_pk12");
	if (buildpk12 == NULL) {
		return (KMF_ERR_FUNCTION_NOT_FOUND);
	}

	rv = buildpk12(handle, numcerts, certlist, numkeys, keylist, p12cred,
	    filename);

	return (rv);
}


KMF_RETURN
kmf_import_objects(KMF_HANDLE_T handle, char *filename,
	KMF_CREDENTIAL *cred,
	KMF_X509_DER_CERT **certs, int *ncerts,
	KMF_RAW_KEY_DATA **rawkeys, int *nkeys)
{
	KMF_RETURN rv;
	KMF_PLUGIN *plugin;
	KMF_RETURN (*import_objects)(KMF_HANDLE *, char *, KMF_CREDENTIAL *,
	    KMF_X509_DER_CERT **, int *, KMF_RAW_KEY_DATA **, int *);

	CLEAR_ERROR(handle, rv);
	if (rv != KMF_OK)
		return (rv);

	if (filename == NULL ||	cred == NULL ||	certs == NULL ||
	    ncerts == NULL ||rawkeys == NULL || nkeys == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/*
	 * Use the Keypair reader from the OpenSSL plugin.
	 */
	plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL);
	if (plugin == NULL || plugin->dldesc == NULL) {
		return (KMF_ERR_PLUGIN_NOTFOUND);
	}

	import_objects = (KMF_RETURN(*)())dlsym(plugin->dldesc,
	    "openssl_import_objects");
	if (import_objects == NULL) {
		return (KMF_ERR_FUNCTION_NOT_FOUND);
	}

	/* Use OpenSSL interfaces to get raw key and cert data */
	rv = import_objects(handle, filename, cred, certs, ncerts,
	    rawkeys, nkeys);

	return (rv);
}

KMF_BOOL
IsEqualOid(KMF_OID *Oid1, KMF_OID *Oid2)
{
	return ((Oid1->Length == Oid2->Length) &&
	    !memcmp(Oid1->Data, Oid2->Data, Oid1->Length));
}

static KMF_RETURN
set_algoid(KMF_X509_ALGORITHM_IDENTIFIER *destid,
	KMF_OID *newoid)
{
	if (destid == NULL || newoid == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	destid->algorithm.Length = newoid->Length;
	destid->algorithm.Data = malloc(destid->algorithm.Length);
	if (destid->algorithm.Data == NULL)
		return (KMF_ERR_MEMORY);

	(void) memcpy(destid->algorithm.Data, newoid->Data,
	    destid->algorithm.Length);

	return (KMF_OK);
}

KMF_RETURN
copy_algoid(KMF_X509_ALGORITHM_IDENTIFIER *destid,
	KMF_X509_ALGORITHM_IDENTIFIER *srcid)
{
	KMF_RETURN ret = KMF_OK;
	if (!destid || !srcid)
		return (KMF_ERR_BAD_PARAMETER);

	destid->algorithm.Length = srcid->algorithm.Length;
	destid->algorithm.Data = malloc(destid->algorithm.Length);
	if (destid->algorithm.Data == NULL)
		return (KMF_ERR_MEMORY);

	(void) memcpy(destid->algorithm.Data, srcid->algorithm.Data,
	    destid->algorithm.Length);

	destid->parameters.Length = srcid->parameters.Length;
	if (destid->parameters.Length > 0) {
		destid->parameters.Data = malloc(destid->parameters.Length);
		if (destid->parameters.Data == NULL)
			return (KMF_ERR_MEMORY);

		(void) memcpy(destid->parameters.Data, srcid->parameters.Data,
		    destid->parameters.Length);
	} else {
		destid->parameters.Data = NULL;
	}
	return (ret);
}

static KMF_RETURN
sign_cert(KMF_HANDLE_T handle,
	const KMF_DATA *SubjectCert,
	KMF_KEY_HANDLE	*Signkey,
	KMF_OID		*signature_oid,
	KMF_DATA	*SignedCert)
{
	KMF_X509_CERTIFICATE	*subj_cert = NULL;
	KMF_DATA		data_to_sign = {0, NULL};
	KMF_DATA		signed_data = {0, NULL};
	KMF_RETURN		ret = KMF_OK;
	KMF_ALGORITHM_INDEX	algid;
	int i = 0;
	KMF_ATTRIBUTE attrlist[8];

	if (!SignedCert)
		return (KMF_ERR_BAD_PARAMETER);

	SignedCert->Length = 0;
	SignedCert->Data = NULL;

	if (!SubjectCert)
		return (KMF_ERR_BAD_PARAMETER);

	if (!SubjectCert->Data || !SubjectCert->Length)
		return (KMF_ERR_BAD_PARAMETER);

	/*
	 * Shortcut - just extract the already encoded TBS cert data from
	 * the original data buffer.  Since we haven't changed anything,
	 * there is no need to re-encode it.
	 */
	ret = ExtractX509CertParts((KMF_DATA *)SubjectCert,
	    &data_to_sign, NULL);
	if (ret != KMF_OK) {
		goto cleanup;
	}

	/* Estimate the signed data length generously */
	signed_data.Length = data_to_sign.Length*2;
	signed_data.Data = calloc(1, signed_data.Length);
	if (!signed_data.Data) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	/*
	 * If we got here OK, decode into a structure and then re-encode
	 * the complete certificate.
	 */
	ret = DerDecodeSignedCertificate(SubjectCert, &subj_cert);
	if (ret != KMF_OK) {
		goto cleanup;
	}

	/* We are re-signing this cert, so clear out old signature data */
	if (subj_cert->signature.algorithmIdentifier.algorithm.Length == 0) {
		kmf_free_algoid(&subj_cert->signature.algorithmIdentifier);
		ret = set_algoid(&subj_cert->signature.algorithmIdentifier,
		    signature_oid);
		if (ret != KMF_OK)
			goto cleanup;
		ret = set_algoid(&subj_cert->certificate.signature,
		    signature_oid);
		if (ret)
			goto cleanup;
	}
	kmf_set_attr_at_index(attrlist, i, KMF_KEYSTORE_TYPE_ATTR,
	    &Signkey->kstype, sizeof (KMF_KEYSTORE_TYPE));
	i++;
	kmf_set_attr_at_index(attrlist, i, KMF_KEY_HANDLE_ATTR,
	    Signkey, sizeof (KMF_KEY_HANDLE));
	i++;
	kmf_set_attr_at_index(attrlist, i, KMF_DATA_ATTR,
	    &data_to_sign, sizeof (KMF_DATA));
	i++;
	kmf_set_attr_at_index(attrlist, i, KMF_OUT_DATA_ATTR,
	    &signed_data, sizeof (KMF_DATA));
	i++;
	kmf_set_attr_at_index(attrlist, i, KMF_OID_ATTR,
	    signature_oid, sizeof (KMF_OID));
	i++;

	/* Sign the data */
	ret = kmf_sign_data(handle, i, attrlist);

	if (ret != KMF_OK)
		goto cleanup;

	algid = x509_algoid_to_algid(signature_oid);

	/*
	 * For DSA, KMF_SignDataWithKey() returns a 40-bytes decoded
	 * signature. So we must encode the signature correctly.
	 */
	if (algid == KMF_ALGID_SHA1WithDSA) {

		KMF_DATA signature;

		ret = DerEncodeDSASignature(&signed_data, &signature);
		kmf_free_data(&signed_data);

		if (ret != KMF_OK)
			goto cleanup;

		subj_cert->signature.encrypted = signature;
	} else {
		subj_cert->signature.encrypted = signed_data;
	}

	/* Now, re-encode the cert with the new signature */
	ret = DerEncodeSignedCertificate(subj_cert, SignedCert);

cleanup:
	/* Cleanup & return */
	if (ret != KMF_OK)
		kmf_free_data(SignedCert);

	kmf_free_data(&data_to_sign);

	if (subj_cert != NULL) {
		kmf_free_signed_cert(subj_cert);
		free(subj_cert);
	}

	return (ret);
}

static KMF_RETURN
verify_cert_with_key(KMF_HANDLE_T handle,
	KMF_DATA *derkey,
	const KMF_DATA *CertToBeVerified)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_CERTIFICATE *signed_cert = NULL;
	KMF_X509_SPKI	spki;
	KMF_DATA	data_to_verify = {0, NULL};
	KMF_DATA	signed_data = {0, NULL};
	KMF_DATA	signature = { 0, NULL };
	KMF_ALGORITHM_INDEX	algid;

	/* check the caller and do other setup for this SPI call */
	if (handle == NULL || CertToBeVerified == NULL ||
	    derkey == NULL || derkey->Data == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	(void) memset(&spki, 0, sizeof (KMF_X509_SPKI));

	ret = ExtractX509CertParts((KMF_DATA *)CertToBeVerified,
	    &data_to_verify, &signed_data);

	if (ret != KMF_OK)
		goto cleanup;

	ret = DerDecodeSPKI(derkey, &spki);
	if (ret != KMF_OK)
		goto cleanup;

	/* Decode the signer cert so we can get the Algorithm data */
	ret = DerDecodeSignedCertificate(CertToBeVerified, &signed_cert);
	if (ret != KMF_OK)
		return (ret);

	algid = x509_algoid_to_algid(CERT_SIG_OID(signed_cert));

	if (algid == KMF_ALGID_NONE)
		return (KMF_ERR_BAD_ALGORITHM);

	if (algid == KMF_ALGID_SHA1WithDSA) {
		ret = DerDecodeDSASignature(&signed_data, &signature);
		if (ret != KMF_OK)
			goto cleanup;
	} else {
		signature.Data = signed_data.Data;
		signature.Length = signed_data.Length;
	}

	ret = PKCS_VerifyData(handle, algid, &spki,
	    &data_to_verify, &signature);

cleanup:
	if (data_to_verify.Data != NULL)
		free(data_to_verify.Data);

	if (signed_data.Data != NULL)
		free(signed_data.Data);

	if (signed_cert) {
		kmf_free_signed_cert(signed_cert);
		free(signed_cert);
	}
	if (algid == KMF_ALGID_SHA1WithDSA) {
		free(signature.Data);
	}

	kmf_free_algoid(&spki.algorithm);
	kmf_free_data(&spki.subjectPublicKey);

	return (ret);
}

/*
 * Use a signer cert to verify another certificate's signature.
 * This code forces the use of the PKCS11 mechanism for the verify
 * operation for the Cryptographic Framework's FIPS-140 boundary.
 */
static KMF_RETURN
verify_cert_with_cert(KMF_HANDLE_T handle,
	const KMF_DATA *CertToBeVerifiedData,
	const KMF_DATA *SignerCertData)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_CERTIFICATE *SignerCert = NULL;
	KMF_X509_CERTIFICATE *ToBeVerifiedCert = NULL;
	KMF_DATA	data_to_verify = {0, NULL};
	KMF_DATA	signed_data = {0, NULL};
	KMF_DATA	signature;
	KMF_ALGORITHM_INDEX	algid;
	KMF_POLICY_RECORD	*policy;

	if (handle == NULL ||
	    !CertToBeVerifiedData ||
	    !CertToBeVerifiedData->Data ||
	    !CertToBeVerifiedData->Length)
		return (KMF_ERR_BAD_PARAMETER);

	if (!SignerCertData ||
	    !SignerCertData->Data ||
	    !SignerCertData->Length)
		return (KMF_ERR_BAD_PARAMETER);

	policy = handle->policy;

	/* Make sure the signer has proper key usage bits */
	ret = check_key_usage(handle, SignerCertData, KMF_KU_SIGN_CERT);
	if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
		ret = KMF_OK;
	if (ret != KMF_OK)
		return (ret);

	/* Decode the cert into parts for verification */
	ret = ExtractX509CertParts((KMF_DATA *)CertToBeVerifiedData,
	    &data_to_verify, &signed_data);
	if (ret != KMF_OK)
		goto cleanup;

	/* Decode the to-be-verified cert so we know what algorithm to use */
	ret = DerDecodeSignedCertificate(CertToBeVerifiedData,
	    &ToBeVerifiedCert);

	if (ret != KMF_OK)
		goto cleanup;

	algid = x509_algoid_to_algid(CERT_SIG_OID(ToBeVerifiedCert));

	if (algid == KMF_ALGID_SHA1WithDSA) {
		ret = DerDecodeDSASignature(&signed_data, &signature);
		if (ret != KMF_OK)
			goto cleanup;
	} else {
		signature.Data = signed_data.Data;
		signature.Length = signed_data.Length;
	}

	/*
	 * Force use of PKCS11 API for kcfd/libelfsign.  This is
	 * required for the Cryptographic Framework's FIPS-140 boundary.
	 */
	ret = plugin_verify_data_with_cert(handle, KMF_KEYSTORE_PK11TOKEN,
	    algid, &data_to_verify, &signature,	SignerCertData);

cleanup:
	kmf_free_data(&data_to_verify);
	kmf_free_data(&signed_data);

	if (SignerCert) {
		kmf_free_signed_cert(SignerCert);
		free(SignerCert);
	}

	if (ToBeVerifiedCert) {
		kmf_free_signed_cert(ToBeVerifiedCert);
		free(ToBeVerifiedCert);
	}

	if (algid == KMF_ALGID_SHA1WithDSA) {
		free(signature.Data);
	}

	return (ret);
}

/*
 * Phase 1 APIs still needed to maintain compat with elfsign.
 */
KMF_RETURN
KMF_VerifyDataWithCert(KMF_HANDLE_T handle,
	KMF_KEYSTORE_TYPE kstype,
	KMF_ALGORITHM_INDEX algid,
	KMF_DATA *indata,
	KMF_DATA *insig,
	const KMF_DATA *SignerCert)
{
	KMF_ATTRIBUTE attrlist[8];
	int numattr = 0;

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR,
	    &kstype,  sizeof (kstype));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_DATA_ATTR,
	    indata, sizeof (KMF_DATA));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_IN_SIGN_ATTR,
	    insig, sizeof (KMF_DATA));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_SIGNER_CERT_DATA_ATTR,
	    (KMF_DATA *)SignerCert, sizeof (KMF_DATA));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_ALGORITHM_INDEX_ATTR,
	    &algid, sizeof (algid));
	numattr++;

	return (kmf_verify_data(handle, numattr, attrlist));
}

KMF_RETURN
KMF_VerifyCertWithCert(KMF_HANDLE_T handle,
	const KMF_DATA *CertToBeVerified,
	const KMF_DATA *SignerCert)
{
	if (CertToBeVerified == NULL || SignerCert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	return (verify_cert_with_cert(handle, CertToBeVerified,
	    SignerCert));
}

KMF_RETURN
KMF_FindCert(KMF_HANDLE_T handle, KMF_FINDCERT_PARAMS *target,
		KMF_X509_DER_CERT *kmf_cert,
		uint32_t *num_certs)
{
	KMF_ATTRIBUTE attrlist[32];
	int i = 0;

	if (target == NULL || num_certs == NULL)
		return (KMF_ERR_BAD_PARAMETER); /* ILLEGAL ARGS ERROR */

	if ((target->find_cert_validity < KMF_ALL_CERTS) ||
	    (target->find_cert_validity > KMF_EXPIRED_CERTS))
		return (KMF_ERR_BAD_PARAMETER);

	kmf_set_attr_at_index(attrlist, i,
	    KMF_KEYSTORE_TYPE_ATTR, &target->kstype, sizeof (target->kstype));
	i++;

	if (kmf_cert != NULL) {
		kmf_set_attr_at_index(attrlist, i,
		    KMF_X509_DER_CERT_ATTR, kmf_cert,
		    sizeof (KMF_X509_DER_CERT));
		i++;
	}

	kmf_set_attr_at_index(attrlist, i,
	    KMF_COUNT_ATTR, num_certs, sizeof (uint32_t));
	i++;

	/* Set the optional searching attributes for all 3 plugins. */
	if (target->issuer != NULL) {
		kmf_set_attr_at_index(attrlist, i, KMF_ISSUER_NAME_ATTR,
		    target->issuer, strlen(target->issuer));
		i++;
	}
	if (target->subject != NULL) {
		kmf_set_attr_at_index(attrlist, i, KMF_SUBJECT_NAME_ATTR,
		    target->subject, strlen(target->subject));
		i++;
	}
	if (target->serial != NULL) {
		kmf_set_attr_at_index(attrlist, i, KMF_BIGINT_ATTR,
		    target->serial, sizeof (KMF_BIGINT));
		i++;
	}

	kmf_set_attr_at_index(attrlist, i, KMF_CERT_VALIDITY_ATTR,
	    &target->find_cert_validity, sizeof (KMF_CERT_VALIDITY));
	i++;

	if (target->kstype == KMF_KEYSTORE_NSS) {
		if (target->certLabel != NULL) {
			kmf_set_attr_at_index(attrlist, i,
			    KMF_CERT_LABEL_ATTR,
			    target->certLabel, strlen(target->certLabel));
			i++;
		}

		if (target->nssparms.slotlabel != NULL) {
			kmf_set_attr_at_index(attrlist, i,
			    KMF_TOKEN_LABEL_ATTR,
			    target->nssparms.slotlabel,
			    strlen(target->nssparms.slotlabel));
			i++;
		}

	} else if (target->kstype == KMF_KEYSTORE_OPENSSL) {
		if (target->sslparms.certfile != NULL) {
			kmf_set_attr_at_index(attrlist, i,
			    KMF_CERT_FILENAME_ATTR,
			    target->sslparms.certfile,
			    strlen(target->sslparms.certfile));
			i++;
		}

		if (target->sslparms.dirpath != NULL) {
			kmf_set_attr_at_index(attrlist, i,
			    KMF_DIRPATH_ATTR,
			    target->sslparms.dirpath,
			    strlen(target->sslparms.dirpath));
			i++;
		}

	} else if (target->kstype == KMF_KEYSTORE_PK11TOKEN) {
		if (target->certLabel != NULL) {
			kmf_set_attr_at_index(attrlist, i,
			    KMF_CERT_LABEL_ATTR,
			    target->certLabel, strlen(target->certLabel));
			i++;
		}

		kmf_set_attr_at_index(attrlist, i, KMF_PRIVATE_BOOL_ATTR,
		    &target->pkcs11parms.private,
		    sizeof (target->pkcs11parms.private));
		i++;
	}

	return (kmf_find_cert(handle, i, attrlist));
}