view usr/src/lib/libkmf/libkmf/common/certgetsetop.c @ 5051:cbbb7c8b40a9

PSARC 2007/426 KMFAPI Interface Taxonomy Change PSARC 2007/465 pktool symmetric key enhancements 6546405 KMF Interfaces need to be extensible 6547894 pktool should be more detailed 6590232 pktool should import and export generic keys
author wyllys
date Fri, 14 Sep 2007 12:13:39 -0700
parents 376d3096844b
children 865d075cefb7
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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#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 <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ber_der.h>
#include <kmfapiP.h>
#include <libgen.h>
#include <cryptoutil.h>

KMF_RETURN
copy_data(KMF_DATA *dst, KMF_DATA *src)
{
	KMF_RETURN ret = KMF_OK;

	if (dst == NULL || src == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	dst->Data = malloc(src->Length);
	if (dst->Data == NULL)
		return (KMF_ERR_MEMORY);

	dst->Length = src->Length;
	(void) memcpy(dst->Data, src->Data, src->Length);

	return (ret);
}

static KMF_RETURN
copy_extension_data(KMF_X509_EXTENSION *dstext,
	KMF_X509_EXTENSION *srcext)
{
	KMF_RETURN ret = KMF_OK;

	if (dstext == NULL || srcext == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	(void) memset(dstext, 0, sizeof (KMF_X509_EXTENSION));

	ret = copy_data(&dstext->extnId, &srcext->extnId);
	if (ret != KMF_OK)
		goto cleanup;

	dstext->extnId.Length = srcext->extnId.Length;
	dstext->critical = srcext->critical;
	dstext->format = srcext->format;

	ret = copy_data(&dstext->BERvalue, &srcext->BERvalue);
	if (ret != KMF_OK)
		goto cleanup;

	dstext->value.tagAndValue = malloc(sizeof (KMF_X509EXT_TAGandVALUE));
	if (dstext->value.tagAndValue == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	(void) memset(dstext->value.tagAndValue, 0,
	    sizeof (KMF_X509EXT_TAGandVALUE));

	ret = copy_data(&dstext->value.tagAndValue->value,
	    &srcext->value.tagAndValue->value);
	if (ret != KMF_OK)
		goto cleanup;

	dstext->value.tagAndValue->type = srcext->value.tagAndValue->type;

cleanup:
	if (ret != KMF_OK) {
		if (dstext->extnId.Data != NULL)
			kmf_free_data(&dstext->extnId);

		if (dstext->BERvalue.Data != NULL)
			kmf_free_data(&dstext->BERvalue);

		if (dstext->value.tagAndValue->value.Data == NULL)
			kmf_free_data(&dstext->value.tagAndValue->value);
	}

	return (ret);
}

/*
 * Given a block of DER encoded X.509 certificate data and
 * an OID for the desired extension, this routine will
 * parse the cert data and return the data associated with
 * the extension if it is found.
 *
 * RETURNS:
 *   KMF_OK - if extension found and copied OK.
 *   KMF_ERR_EXTENSION_NOT_FOUND - extension not found.
 *   parsing and memory allocation errors are also possible.
 */
KMF_RETURN
kmf_get_cert_extn(const KMF_DATA *certdata,
	KMF_OID *extoid, KMF_X509_EXTENSION *extdata)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_CERTIFICATE *cert = NULL;
	KMF_X509_EXTENSION *eptr = NULL;
	int i, found = 0;

	if (certdata == NULL || extoid == NULL || extdata == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	ret = DerDecodeSignedCertificate(certdata, &cert);
	if (ret != KMF_OK)
		return (ret);

	if (cert->certificate.extensions.numberOfExtensions == 0)
		return (KMF_ERR_EXTENSION_NOT_FOUND);

	(void) memset((void *)extdata, 0, sizeof (KMF_X509_EXTENSION));
	for (i = 0; !found &&
	    i < cert->certificate.extensions.numberOfExtensions;
	    i++) {
		eptr = &cert->certificate.extensions.extensions[i];
		if (IsEqualOid(extoid, &eptr->extnId)) {
			ret = copy_extension_data(extdata, eptr);
			found++;
		}
	}
	if (!found)
		ret = KMF_ERR_EXTENSION_NOT_FOUND;

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

	return (ret);
}

/*
 * Given a block of DER encoded X.509 certificate data and
 * a "crit/non-crit/all" flag, search the extensions and
 * return the OIDs for critical, non-critical or all extensions.
 *
 * RETURNS:
 *   KMF_OK - if extension found and copied OK.
 *   parsing and memory allocation errors are also possible.
 *
 *   OIDlist - array of KMF_OID records, allocated
 *             by this function.
 *   NumOIDs - number of critical extensions found.
 */
KMF_RETURN
kmf_get_cert_extns(const KMF_DATA *certdata, KMF_FLAG_CERT_EXTN flag,
	KMF_X509_EXTENSION **extlist, int *nextns)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_CERTIFICATE *cert;
	KMF_X509_EXTENSION *eptr, *elist;
	int i;

	if (certdata == NULL || extlist == NULL || nextns == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (flag < KMF_ALL_EXTNS || flag > KMF_NONCRITICAL_EXTNS)
		return (KMF_ERR_BAD_PARAMETER);

	*nextns = 0;
	*extlist = elist = NULL;
	ret = DerDecodeSignedCertificate(certdata, &cert);
	if (ret != KMF_OK)
		return (ret);

	if (cert->certificate.extensions.numberOfExtensions == 0)
		return (KMF_ERR_EXTENSION_NOT_FOUND);

	for (i = 0; i < cert->certificate.extensions.numberOfExtensions;
	    i++) {
		eptr = &cert->certificate.extensions.extensions[i];

		if (flag == KMF_CRITICAL_EXTNS && eptr->critical == 0)
			continue;
		else if (flag == KMF_NONCRITICAL_EXTNS && eptr->critical != 0)
			continue;

		(*nextns)++;
		elist = realloc(elist, sizeof (KMF_X509_EXTENSION) *
		    (*nextns));
		if (elist == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}

		ret = copy_extension_data(&elist[(*nextns) - 1], eptr);
		if (ret != KMF_OK)
			goto end;
	}

end:
	kmf_free_signed_cert(cert);
	free(cert);
	if (ret != KMF_OK) {
		if (elist != NULL) {
			free(elist);
			elist = NULL;
		}
		*nextns = 0;
	}

	/*
	 * If the flag is not all, then it is possible that we did not find
	 * any critical or non_critical extensions.  When that happened,
	 * return KMF_ERR_EXTENSION_NOT_FOUND.
	 */
	if (flag != KMF_ALL_EXTNS && ret == KMF_OK && *nextns == 0)
		ret = KMF_ERR_EXTENSION_NOT_FOUND;

	*extlist = elist;
	return (ret);
}

/*
 * If the given certificate data (X.509 DER encoded data)
 * contains the Key Usage extension, parse that
 * data and return it in the KMF_X509EXT_BASICCONSTRAINTS
 * record.
 *
 * RETURNS:
 *  KMF_OK - success
 *  KMF_ERR_BAD_PARAMETER - input data was bad.
 *  KMF_ERR_EXTENSION_NOT_FOUND - extension not found.
 */
KMF_RETURN
kmf_get_cert_ku(const KMF_DATA *certdata,
	KMF_X509EXT_KEY_USAGE *keyusage)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;

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

	(void) memset(&extn, 0, sizeof (extn));
	/*
	 * Check standard KeyUsage bits
	 */
	ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_KeyUsage, &extn);

	if (ret != KMF_OK) {
		goto end;
	}
	keyusage->critical = (extn.critical != 0);
	if (extn.value.tagAndValue->value.Length > 1) {
		keyusage->KeyUsageBits =
		    extn.value.tagAndValue->value.Data[1] << 8;
	} else  {
		keyusage->KeyUsageBits = extn.value.tagAndValue->value.Data[0];
	}
end:
	kmf_free_extn(&extn);
	return (ret);
}

static KMF_BOOL
isEKUPresent(KMF_X509EXT_EKU *ekuptr, KMF_OID *ekuoid)
{
	int i;

	if (ekuptr == NULL || ekuoid == NULL)
		return (0);

	for (i = 0; i < ekuptr->nEKUs; i++)
		if (IsEqualOid(&ekuptr->keyPurposeIdList[i], ekuoid))
			return (1);

	return (0);
}

static KMF_RETURN
parse_eku_data(const KMF_DATA *asn1data, KMF_X509EXT_EKU *ekuptr)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue exdata;
	KMF_OID oid;
	char *end = NULL;
	ber_len_t size;

	/*
	 * Decode the ASN.1 data for the extension.
	 */
	exdata.bv_val = (char *)asn1data->Data;
	exdata.bv_len = asn1data->Length;

	if ((asn1 = kmfder_init(&exdata)) == NULL) {
		ret = KMF_ERR_MEMORY;
		goto end;
	}

	/*
	 * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
	 */
	if (kmfber_first_element(asn1, &size, &end) != BER_OBJECT_IDENTIFIER) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto end;
	}

	/*
	 * Count the number of EKU OIDs and store in
	 * the array.
	 */
	while (kmfber_next_element(asn1, &size, end) ==
	    BER_OBJECT_IDENTIFIER) {

		/* Skip over the CONSTRUCTED SET tag */
		if (kmfber_scanf(asn1, "D", &oid) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}
		ekuptr->nEKUs++;
		ekuptr->keyPurposeIdList = realloc(ekuptr->keyPurposeIdList,
		    ekuptr->nEKUs * sizeof (KMF_OID));
		if (ekuptr->keyPurposeIdList == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}
		ekuptr->keyPurposeIdList[ekuptr->nEKUs - 1] = oid;
	}

end:
	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	if (ret != KMF_OK) {
		if (ekuptr->keyPurposeIdList != NULL) {
			free_keyidlist(ekuptr->keyPurposeIdList, ekuptr->nEKUs);
			ekuptr->keyPurposeIdList = NULL;
			ekuptr->critical = 0;
		}
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_eku(const KMF_DATA *certdata,
	KMF_X509EXT_EKU *ekuptr)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;

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

	(void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION));

	ekuptr->nEKUs = 0;
	ekuptr->keyPurposeIdList = NULL;
	ekuptr->critical = 0;

	ret = kmf_get_cert_extn(certdata,
	    (KMF_OID *)&KMFOID_ExtendedKeyUsage, &extn);

	if (ret != KMF_OK) {
		goto end;
	}

	ret = parse_eku_data(&extn.BERvalue, ekuptr);

end:
	kmf_free_extn(&extn);

	return (ret);
}

/*
 * If the given certificate data (X.509 DER encoded data)
 * contains the Basic Constraints extension, parse that
 * data and return it in the KMF_X509EXT_BASICCONSTRAINTS
 * record.
 *
 * RETURNS:
 *  KMF_OK - success
 *  KMF_ERR_BAD_PARAMETER - input data was bad.
 *  KMF_ERR_EXTENSION_NOT_FOUND - extension not found.
 */
KMF_RETURN
kmf_get_cert_basic_constraint(const KMF_DATA *certdata,
	KMF_BOOL *critical, KMF_X509EXT_BASICCONSTRAINTS *constraint)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;
	BerElement *asn1 = NULL;
	BerValue exdata;
	ber_len_t size;
	char *end = NULL;
	int tag;

	if (certdata == NULL || constraint == NULL || critical == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	(void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION));
	ret = kmf_get_cert_extn(certdata,
	    (KMF_OID *)&KMFOID_BasicConstraints, &extn);

	if (ret != KMF_OK) {
		goto end;
	}

	*critical = (extn.critical != 0);

	exdata.bv_val = (char *)extn.value.tagAndValue->value.Data;
	exdata.bv_len = extn.value.tagAndValue->value.Length;

	if ((asn1 = kmfder_init(&exdata)) == NULL) {
		ret = KMF_ERR_MEMORY;
		goto end;
	}

	if (kmfber_scanf(asn1, "b", &constraint->cA) == KMFBER_DEFAULT) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto end;
	}
	constraint->pathLenConstraintPresent = KMF_FALSE;

	tag = kmfber_next_element(asn1, &size, end);
	if (tag == BER_INTEGER) {
		if (kmfber_scanf(asn1, "i",
		    &constraint->pathLenConstraint) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}
		constraint->pathLenConstraintPresent = KMF_TRUE;
	}
end:
	kmf_free_extn(&extn);
	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	return (ret);
}

static KMF_X509EXT_POLICYQUALIFIERINFO *
get_pqinfo(BerElement *asn1)
{
	KMF_X509EXT_POLICYQUALIFIERINFO *pqinfo = NULL;
	KMF_RETURN ret = KMF_OK;
	int tag;
	ber_len_t size;
	char *end = NULL;

	/*
	 * Policy Qualifiers may be a list of sequences.
	 *
	 * PolicyInformation ::= SEQUENCE {
	 * 	policyIdentifier   CertPolicyId,
	 * 	policyQualifiers   SEQUENCE SIZE (1..MAX) OF
	 *			PolicyQualifierInfo OPTIONAL
	 * }
	 *
	 * PolicyQualifierInfo ::= SEQUENCE {
	 *	policyQualifierId  PolicyQualifierId,
	 *	qualifier	  ANY DEFINED BY policyQualifierId
	 * }
	 */


	/*
	 * We already got the CertPolicyId, we just need to
	 * find all of the policyQualifiers in the set.
	 *
	 * Mark the first element of the SEQUENCE and reset the end ptr
	 * so the ber/der code knows when to stop looking.
	 */
	if ((tag = kmfber_first_element(asn1, &size, &end)) !=
	    BER_CONSTRUCTED_SEQUENCE) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto end;
	}
	/* We found a sequence, loop until done */
	while ((tag = kmfber_next_element(asn1, &size, end)) ==
	    BER_CONSTRUCTED_SEQUENCE) {

		/* Skip over the CONSTRUCTED SET tag */
		if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}
		/*
		 * Allocate memory for the Policy Qualifier Info
		 */
		pqinfo = malloc(sizeof (KMF_X509EXT_POLICYQUALIFIERINFO));
		if (pqinfo == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}
		(void) memset((void *)pqinfo, 0,
		    sizeof (KMF_X509EXT_POLICYQUALIFIERINFO));
		/*
		 * Read the PolicyQualifier OID
		 */
		if (kmfber_scanf(asn1, "D",
		    &pqinfo->policyQualifierId) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}
		/*
		 * The OID of the policyQualifierId determines what
		 * sort of data comes next.
		 */
		if (IsEqualOid(&pqinfo->policyQualifierId,
		    (KMF_OID *)&KMFOID_PKIX_PQ_CPSuri)) {
			/*
			 * CPS uri must be an IA5STRING
			 */
			if (kmfber_scanf(asn1, "tl", &tag, &size) ==
			    KMFBER_DEFAULT || tag != BER_IA5STRING ||
			    size == 0) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}
			if ((pqinfo->value.Data = malloc(size)) == NULL) {
				ret = KMF_ERR_MEMORY;
				goto end;
			}
			if (kmfber_scanf(asn1, "s", pqinfo->value.Data,
			    &pqinfo->value.Length) == KMFBER_DEFAULT) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}
		} else if (IsEqualOid(&pqinfo->policyQualifierId,
		    (KMF_OID *)&KMFOID_PKIX_PQ_Unotice)) {
			if (kmfber_scanf(asn1, "tl", &tag, &size) ==
			    KMFBER_DEFAULT ||
			    tag != BER_CONSTRUCTED_SEQUENCE) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}
			/*
			 * For now, just copy the while UserNotice ASN.1
			 * blob into the pqinfo data record.
			 * TBD - parse it into individual fields.
			 */
			if ((pqinfo->value.Data = malloc(size)) == NULL) {
				ret = KMF_ERR_MEMORY;
				goto end;
			}
			if (kmfber_scanf(asn1, "s", pqinfo->value.Data,
			    &pqinfo->value.Length) == KMFBER_DEFAULT) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}
		} else {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}
	}
end:
	if (ret != KMF_OK) {
		if (pqinfo != NULL) {
			kmf_free_data(&pqinfo->value);
			kmf_free_data(&pqinfo->policyQualifierId);
			free(pqinfo);
			pqinfo = NULL;
		}
	}
	return (pqinfo);
}

/*
 * If the given certificate data (X.509 DER encoded data)
 * contains the Certificate Policies extension, parse that
 * data and return it in the KMF_X509EXT_CERT_POLICIES
 * record.
 *
 * RETURNS:
 *  KMF_OK - success
 *  KMF_ERR_BAD_PARAMETER - input data was bad.
 *  KMF_ERR_EXTENSION_NOT_FOUND - extension not found.
 *  parsing and memory allocation errors are also possible.
 */
KMF_RETURN
kmf_get_cert_policies(const KMF_DATA *certdata,
	KMF_BOOL *critical, KMF_X509EXT_CERT_POLICIES *extptr)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;
	KMF_X509EXT_POLICYINFO	*pinfo;
	KMF_X509EXT_POLICYQUALIFIERINFO *pqinfo;
	BerElement *asn1 = NULL;
	BerValue exdata;
	ber_len_t size;
	char *end = NULL;
	int tag;

	if (certdata == NULL || critical == NULL || extptr == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	(void) memset(&extn, 0, sizeof (extn));
	ret = kmf_get_cert_extn(certdata,
	    (KMF_OID *)&KMFOID_CertificatePolicies, &extn);

	if (ret != KMF_OK) {
		goto end;
	}

	*critical = (extn.critical != 0);

	/*
	 * Decode the ASN.1 data for the extension.
	 */
	exdata.bv_val = (char *)extn.BERvalue.Data;
	exdata.bv_len = extn.BERvalue.Length;

	(void) memset((void *)extptr, 0, sizeof (KMF_X509EXT_CERT_POLICIES));

	if ((asn1 = kmfder_init(&exdata)) == NULL) {
		ret = KMF_ERR_MEMORY;
		goto end;
	}

	/*
	 * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
	 */
	if ((tag = kmfber_first_element(asn1, &size, &end)) !=
	    BER_CONSTRUCTED_SEQUENCE) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto end;
	}

	/*
	 * Collect all of the PolicyInformation SEQUENCES
	 *
	 * PolicyInformation ::= SEQUENCE {
	 * 	policyIdentifier   CertPolicyId,
	 * 	policyQualifiers   SEQUENCE SIZE (1..MAX) OF
	 *			PolicyQualifierInfo OPTIONAL
	 * }
	 *
	 * Loop over the SEQUENCES of PolicyInfo
	 */
	while ((tag = kmfber_next_element(asn1, &size, end)) ==
	    BER_CONSTRUCTED_SEQUENCE) {

		/* Skip over the CONSTRUCTED SET tag */
		if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}

		pinfo = malloc(sizeof (KMF_X509EXT_POLICYINFO));
		if (pinfo == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}
		(void) memset((void *)pinfo, 0,
		    sizeof (KMF_X509EXT_POLICYINFO));
		/*
		 * Decode the PolicyInformation SEQUENCE
		 */
		if ((tag = kmfber_scanf(asn1, "D",
		    &pinfo->policyIdentifier)) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}
		/*
		 * Gather all of the associated PolicyQualifierInfo recs
		 */
		pqinfo = get_pqinfo(asn1);
		if (pqinfo != NULL) {
			int cnt =
			    pinfo->policyQualifiers.numberOfPolicyQualifiers;
			cnt++;
			pinfo->policyQualifiers.policyQualifier = realloc(
			    pinfo->policyQualifiers.policyQualifier,
			    cnt * sizeof (KMF_X509EXT_POLICYQUALIFIERINFO));
			if (pinfo->policyQualifiers.policyQualifier == NULL) {
				ret = KMF_ERR_MEMORY;
				goto end;
			}
			pinfo->policyQualifiers.numberOfPolicyQualifiers = cnt;
			pinfo->policyQualifiers.policyQualifier[cnt-1] =
			    *pqinfo;

			free(pqinfo);
		}
		extptr->numberOfPolicyInfo++;
		extptr->policyInfo = realloc(extptr->policyInfo,
		    extptr->numberOfPolicyInfo *
		    sizeof (KMF_X509EXT_POLICYINFO));
		if (extptr->policyInfo == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}
		extptr->policyInfo[extptr->numberOfPolicyInfo-1] = *pinfo;
		free(pinfo);
	}


end:
	kmf_free_extn(&extn);
	if (asn1 != NULL)
		kmfber_free(asn1, 1);
	return (ret);
}

/*
 * If the given certificate data (X.509 DER encoded data)
 * contains the Authority Information Access extension, parse that
 * data and return it in the KMF_X509EXT_AUTHINFOACCESS
 * record.
 *
 * RETURNS:
 *  KMF_OK - success
 *  KMF_ERR_BAD_PARAMETER - input data was bad.
 *  KMF_ERR_EXTENSION_NOT_FOUND - extension not found.
 */
KMF_RETURN
kmf_get_cert_auth_info_access(const KMF_DATA *certdata,
	KMF_X509EXT_AUTHINFOACCESS *aia)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;
	BerElement *asn1 = NULL;
	BerValue exdata;
	ber_len_t size;
	char *end = NULL;
	int tag;
	KMF_X509EXT_ACCESSDESC *access_info = NULL;

	if (certdata == NULL || aia == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	(void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION));
	ret = kmf_get_cert_extn(certdata,
	    (KMF_OID *)&KMFOID_AuthorityInfoAccess, &extn);

	if (ret != KMF_OK) {
		goto end;
	}

	/*
	 * Decode the ASN.1 data for the extension.
	 */
	exdata.bv_val = (char *)extn.BERvalue.Data;
	exdata.bv_len = extn.BERvalue.Length;

	(void) memset((void *)aia, 0, sizeof (KMF_X509EXT_AUTHINFOACCESS));

	if ((asn1 = kmfder_init(&exdata)) == NULL) {
		ret = KMF_ERR_MEMORY;
		goto end;
	}

	/*
	 * AuthorityInfoAccessSyntax  ::=
	 *	SEQUENCE SIZE (1..MAX) OF AccessDescription
	 */
	if ((tag = kmfber_first_element(asn1, &size, &end)) !=
	    BER_CONSTRUCTED_SEQUENCE) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto end;
	}

	/*
	 * AccessDescription  ::=  SEQUENCE {
	 *	accessMethod	OBJECT IDENTIFIER,
	 *	accessLocation	GeneralName  }
	 */
	while ((tag = kmfber_next_element(asn1, &size, end)) ==
	    BER_CONSTRUCTED_SEQUENCE) {

		/* Skip over the CONSTRUCTED SET tag */
		if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}

		access_info = malloc(sizeof (KMF_X509EXT_ACCESSDESC));
		if (access_info == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}

		(void) memset((void *)access_info, 0,
		    sizeof (KMF_X509EXT_ACCESSDESC));

		/*
		 * Read the AccessMethod OID
		 */
		if (kmfber_scanf(asn1, "D",
		    &access_info->AccessMethod) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}

		/*
		 * The OID of the AccessMethod determines what
		 * sort of data comes next.
		 */
		if (IsEqualOid(&access_info->AccessMethod,
		    (KMF_OID *)&KMFOID_PkixAdOcsp)) {
			if (kmfber_scanf(asn1, "tl", &tag, &size) ==
			    KMFBER_DEFAULT || size == 0) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}

			/*
			 * OCSP uri must be an IA5STRING or a GENNAME_URI
			 * with an implicit tag.
			 */
			if (tag != BER_IA5STRING &&
			    tag != (0x80 | GENNAME_URI)) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}

			if ((access_info->AccessLocation.Data =
			    malloc(size)) == NULL) {
				ret = KMF_ERR_MEMORY;
				goto end;
			}

			if (kmfber_scanf(asn1, "s",
			    access_info->AccessLocation.Data,
			    &access_info->AccessLocation.Length) ==
			    KMFBER_DEFAULT) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto end;
			}
		} else if (IsEqualOid(&access_info->AccessMethod,
		    (KMF_OID *)&KMFOID_PkixAdCaIssuers)) {
			/* will be supported later with PKIX */
			free(access_info);
			access_info = NULL;
			continue;
		} else {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto end;
		}

		aia->numberOfAccessDescription++;
		aia->AccessDesc = realloc(aia->AccessDesc,
		    aia->numberOfAccessDescription *
		    sizeof (KMF_X509EXT_ACCESSDESC));

		if (aia->AccessDesc == NULL) {
			ret = KMF_ERR_MEMORY;
			goto end;
		}

		aia->AccessDesc[aia->numberOfAccessDescription-1] =
		    *access_info;
		free(access_info);
		access_info = NULL;
	}

end:
	kmf_free_extn(&extn);
	if (access_info != NULL)
		free(access_info);
	if (asn1 != NULL)
		kmfber_free(asn1, 1);
	return (ret);

}

/*
 * This function parses the name portion of a der-encoded distribution point
 * returns it in the KMF_CRL_DIST_POINT record.
 *
 * The "DistributionPointName" syntax is
 *
 *   DistributionPointName ::= CHOICE {
 *	fullName                [0]     GeneralNames,
 *	nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
 *
 *   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GerneralName
 *
 * Note: for phase 1, we support fullName only.
 */
static KMF_RETURN
parse_dp_name(char *dp_der_code, int dp_der_size, KMF_CRL_DIST_POINT *dp)
{
	KMF_RETURN ret = KMF_OK;
	char *url = NULL;
	BerElement *asn1 = NULL;
	BerValue ber_data;
	ber_len_t size;
	char *end = NULL;
	int tag;
	KMF_GENERALNAMES *fullname;

	if (dp_der_code == NULL || dp_der_size == 0 || dp == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	ber_data.bv_val = dp_der_code;
	ber_data.bv_len = dp_der_size;
	if ((asn1 = kmfder_init(&ber_data)) == NULL)
		return (KMF_ERR_BAD_CERT_FORMAT);

	tag = kmfber_first_element(asn1, &size, &end);
	if (tag != 0xA0 && tag != 0xA1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto out;
	}

	if (tag == 0xA0) { /* fullName */
		dp->type = DP_GENERAL_NAME;

		fullname = &(dp->name.full_name);
		fullname->number = 0;

		/* Skip over the explicit tag and size */
		(void) kmfber_scanf(asn1, "T", &tag);

		tag = kmfber_next_element(asn1, &size, end);
		while (tag != KMFBER_DEFAULT &&
		    tag != KMFBER_END_OF_SEQORSET) {

			if (kmfber_scanf(asn1, "tl", &tag, &size) ==
			    KMFBER_DEFAULT || size == 0) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto out;
			}

			/* For phase 1, we are interested in a URI name only */
			if (tag != (0x80 | GENNAME_URI)) {
				tag = kmfber_next_element(asn1, &size, end);
				continue;
			}

			if ((url = malloc(size)) == NULL) {
				ret = KMF_ERR_MEMORY;
				goto out;
			}

			/* Skip type and len, then read url and save it. */
			if (kmfber_read(asn1, url, 2) != 2) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto out;
			}

			if (kmfber_read(asn1, url, size) !=
			    (ber_slen_t)size) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto out;
			}

			fullname->number++;
			fullname->namelist = realloc(fullname->namelist,
			    fullname->number * sizeof (KMF_GENERALNAME));
			if (fullname->namelist == NULL) {
				ret = KMF_ERR_MEMORY;
				goto out;
			}

			fullname->namelist[fullname->number - 1].choice =
			    GENNAME_URI;
			fullname->namelist[fullname->number - 1].name.Length =
			    size;
			fullname->namelist[fullname->number - 1].name.Data =
			    (unsigned char *)url;

			/* next */
			tag = kmfber_next_element(asn1, &size, end);
		}

	} else if (tag == 0xA1) {
		/* "nameRelativeToCRLIssuer" is not supported at phase 1. */
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto out;
	}

out:
	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	if (ret != KMF_OK) {
		free_dp_name(dp);
	}

	if (ret == KMF_OK && fullname->number == 0) {
		ret = KMF_ERR_EXTENSION_NOT_FOUND;
		if (url != NULL)
			free(url);
	}

	return (ret);
}

/*
 * This function retrieves the CRL Distribution Points extension data from
 * a DER encoded certificate if it contains this extension, parses the
 * extension data, and returns it in the KMF_X509EXT_CRLDISTPOINTS record.
 */
KMF_RETURN
kmf_get_cert_crl_dist_pts(const KMF_DATA *certdata,
	KMF_X509EXT_CRLDISTPOINTS *crl_dps)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;
	BerElement *asn1 = NULL;
	BerValue exdata;
	ber_len_t size;
	char *end = NULL;
	int tag;
	KMF_CRL_DIST_POINT *dp = NULL;
	int i;

	if (certdata == NULL || crl_dps == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	/* Get the ASN.1 data for this extension. */
	(void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION));
	ret = kmf_get_cert_extn(certdata,
	    (KMF_OID *)&KMFOID_CrlDistributionPoints, &extn);
	if (ret != KMF_OK) {
		return (ret);
	}

	/*
	 * Decode the CRLDistributionPoints ASN.1 data. The Syntax for
	 * CRLDistributionPoints is
	 *
	 * CRLDistributionPoints ::=
	 *	SEQUENCE SIZE (1..MAX) OF DistributionPoint
	 *
	 * DistributionPoint ::= SEQUENCE {
	 *	distributionPoint	[0]	DistributionPointName OPTIONAL,
	 *	reasons			[1]	ReasonFlags OPTIONAL,
	 *	cRLIssuer		[2]	GeneralNames OPTIONAL }
	 */

	exdata.bv_val = (char *)extn.BERvalue.Data;
	exdata.bv_len = extn.BERvalue.Length;
	if ((asn1 = kmfder_init(&exdata)) == NULL) {
		ret = KMF_ERR_MEMORY;
		goto out;
	}

	if ((tag = kmfber_first_element(asn1, &size, &end)) !=
	    BER_CONSTRUCTED_SEQUENCE) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto out;
	}

	(void) memset((void *)crl_dps, 0, sizeof (KMF_X509EXT_CRLDISTPOINTS));

	while ((tag = kmfber_next_element(asn1, &size, end)) ==
	    BER_CONSTRUCTED_SEQUENCE) {
		boolean_t has_name = B_FALSE;
		boolean_t has_issuer = B_FALSE;

		/* Skip over the CONSTRUCTED SET tag */
		if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto out;
		}

		tag = kmfber_next_element(asn1, &size, end);
		if (tag != 0xA0 && tag != 0xA1 && tag != 0xA2)
			goto out;

		if ((dp = malloc(sizeof (KMF_CRL_DIST_POINT))) == NULL) {
			ret = KMF_ERR_MEMORY;
			goto out;
		}
		(void) memset((void *)dp, 0, sizeof (KMF_CRL_DIST_POINT));

		if (tag == 0xA0) { /* distributionPoint Name */
			char *name_der;
			int name_size = size + 2;

			if ((name_der = malloc(name_size)) == NULL) {
				ret = KMF_ERR_MEMORY;
				free(dp);
				dp = NULL;
				goto out;
			}

			if (kmfber_read(asn1, name_der, name_size) !=
			    (ber_slen_t)(name_size)) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				free(name_der);
				free(dp);
				dp = NULL;
				goto out;
			}
			has_name = B_TRUE;

			ret = parse_dp_name(name_der, name_size, dp);
			free(name_der);
			if (ret != KMF_OK) {
				free(dp);
				dp = NULL;
				goto out;
			}

			/* next field */
			tag = kmfber_next_element(asn1, &size, end);
		}

		if (tag == 0XA1) { /* reasons */
			char *bit_string;
			int len;

			if (kmfber_scanf(asn1, "B", &bit_string, &len) !=
			    BER_BIT_STRING) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				free(dp);
				dp = NULL;
				goto out;
			}

			dp->reasons.Length = len / 8;
			if ((dp->reasons.Data = malloc(dp->reasons.Length)) ==
			    NULL) {
				ret = KMF_ERR_MEMORY;
				free(dp);
				dp = NULL;
				goto out;
			}
			(void) memcpy(dp->reasons.Data, (uchar_t *)bit_string,
			    dp->reasons.Length);

			/* next field */
			tag = kmfber_next_element(asn1, &size, end);
		}

		if (tag == 0XA2) { /* cRLIssuer */
			char *issuer_der = NULL;
			int issuer_size;

			/* For cRLIssuer, read the data only at phase 1 */
			issuer_size = size + 2;
			issuer_der = malloc(issuer_size);
			if (issuer_der == NULL) {
				ret = KMF_ERR_MEMORY;
				free(dp);
				dp = NULL;
				goto out;
			}

			if (kmfber_read(asn1, issuer_der, issuer_size) !=
			    (ber_slen_t)(issuer_size)) {
				free(issuer_der);
				ret = KMF_ERR_BAD_CERT_FORMAT;
				free(dp);
				dp = NULL;
				goto out;
			}

			has_issuer = B_TRUE;
			free(issuer_der);
		}

		/* A distribution point cannot have a "reasons" field only. */
		if (has_name == B_FALSE && has_issuer == B_FALSE) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			free_dp(dp);
			free(dp);
			dp = NULL;
			goto out;
		}

		/*
		 * Although it is legal that a distributioon point contains
		 * a cRLIssuer field only, with or without "reasons", we will
		 * skip it if the name field is not presented for phase 1.
		 */
		if (has_name == B_FALSE) {
			free_dp(dp);
		} else {
			crl_dps->number++;
			crl_dps->dplist = realloc(crl_dps->dplist,
			    crl_dps->number * sizeof (KMF_CRL_DIST_POINT));
			if (crl_dps->dplist == NULL) {
				ret = KMF_ERR_MEMORY;
				free_dp(dp);
				free(dp);
				dp = NULL;
				goto out;
			}
			crl_dps->dplist[crl_dps->number - 1] = *dp;
			/* free the dp itself since we just used its contents */
		}
		if (dp != NULL) {
			free(dp);
			dp = NULL;
		}
	}

out:
	kmf_free_extn(&extn);

	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	if (ret != KMF_OK) {
		for (i = 0; i < crl_dps->number; i++)
			free_dp(&(crl_dps->dplist[i]));
		free(crl_dps->dplist);
	}

	if (ret == KMF_OK && crl_dps->number == 0) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
	}

	return (ret);
}

static KMF_RETURN
KMF_CertGetPrintable(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
	KMF_PRINTABLE_ITEM flag, char *resultStr)
{
	KMF_PLUGIN *plugin;
	KMF_RETURN (*getPrintableFn)(void *, const KMF_DATA *,
	    KMF_PRINTABLE_ITEM, char *);
	KMF_RETURN ret;

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

	if (SignedCert == NULL || resultStr == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	/*
	 * This framework function is actually implemented in the openssl
	 * plugin library, so we find the function address and call it.
	 */
	plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL);
	if (plugin == NULL || plugin->dldesc == NULL) {
		return (KMF_ERR_PLUGIN_NOTFOUND);
	}

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

	return (getPrintableFn(handle, SignedCert, flag, resultStr));
}

KMF_RETURN
kmf_get_cert_version_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_VERSION,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}


KMF_RETURN
kmf_get_cert_subject_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_SUBJECT,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);

}

KMF_RETURN
kmf_get_cert_issuer_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_ISSUER,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_serial_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_SERIALNUM,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_start_date_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_NOTBEFORE,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_end_date_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
	char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_NOTAFTER,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_pubkey_alg_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_PUBKEY_ALG,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_sig_alg_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_SIGNATURE_ALG,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_pubkey_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_PUBKEY_DATA,
	    tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_email_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
	char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

	if (SignedCert == NULL || result == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_EMAIL, tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

/*
 * Given a certificate (DER Encoded data) and a KMF
 * extension identifier constant (e.g. KMF_X509_EXT_*),
 * return a human readable interpretation of the
 * extension data.
 *
 * The string will be a maximum of KMF_CERT_PRINTABLE_LEN
 * bytes long.  The string is allocated locally and
 * must be freed by the caller.
 */
KMF_RETURN
kmf_get_cert_extn_str(KMF_HANDLE_T handle, const KMF_DATA *cert,
	KMF_PRINTABLE_ITEM extension, char **result)
{
	KMF_RETURN ret;
	char *tmpstr;

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

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

	tmpstr = malloc(KMF_CERT_PRINTABLE_LEN);
	if (tmpstr == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN);

	ret = KMF_CertGetPrintable(handle, cert, extension, tmpstr);

	if (ret == KMF_OK) {
		*result = tmpstr;
	} else {
		free(tmpstr);
		*result = NULL;
	}

	return (ret);
}

KMF_RETURN
kmf_get_cert_id_data(const KMF_DATA *SignedCert, KMF_DATA *ID)
{
	KMF_RETURN ret;
	KMF_X509_CERTIFICATE *cert = NULL;

	if (SignedCert == NULL || ID == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	ret = DerDecodeSignedCertificate(SignedCert, &cert);
	if (ret != KMF_OK)
		return (ret);

	ret = GetIDFromSPKI(&cert->certificate.subjectPublicKeyInfo, ID);

	kmf_free_signed_cert(cert);
	free(cert);
	return (ret);
}

KMF_RETURN
kmf_get_cert_id_str(const KMF_DATA *SignedCert,	char **idstr)
{
	KMF_RETURN ret;
	KMF_DATA ID = {NULL, 0};
	char tmpstr[256];
	int i;

	if (SignedCert == NULL || idstr == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	ret = kmf_get_cert_id_data(SignedCert, &ID);
	if (ret != KMF_OK) {
		kmf_free_data(&ID);
		return (ret);
	}

	(void) memset(tmpstr, 0, sizeof (tmpstr));
	for (i = 0; i < ID.Length; i++) {
		int len = strlen(tmpstr);
		(void) snprintf(&tmpstr[len], sizeof (tmpstr) -  len,
		    "%02x", (uchar_t)ID.Data[i]);
		if ((i+1) < ID.Length)
			(void) strcat(tmpstr, ":");
	}
	*idstr = strdup(tmpstr);
	if ((*idstr) == NULL)
		ret = KMF_ERR_MEMORY;

	kmf_free_data(&ID);

	return (ret);
}


/*
 * This function gets the time_t values of the notbefore and notafter dates
 * from a der-encoded certificate.
 */
KMF_RETURN
kmf_get_cert_validity(const KMF_DATA *cert, time_t *not_before,
    time_t *not_after)
{
	KMF_RETURN rv = KMF_OK;
	KMF_X509_CERTIFICATE *certData = NULL;
	struct tm tm_tmp;
	time_t t_notbefore;
	time_t t_notafter;
	unsigned char *not_before_str;
	unsigned char *not_after_str;

	if (cert == NULL || not_before == NULL || not_after == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	rv = DerDecodeSignedCertificate(cert, &certData);
	if (rv != KMF_OK)
		return (rv);

	/* Get notBefore */
	not_before_str = certData->certificate.validity.notBefore.time.Data;
	if (strptime((const char *)not_before_str, "%y %m %d %H %M %S",
	    &tm_tmp) == NULL) {
		rv = KMF_ERR_VALIDITY_PERIOD;
		goto out;
	}

	errno = 0;
	if (((t_notbefore = mktime(&tm_tmp)) == (time_t)(-1)) &&
	    errno == EOVERFLOW) {
		rv = KMF_ERR_VALIDITY_PERIOD;
		goto out;
	}
	*not_before = t_notbefore;

	/* Get notAfter */
	not_after_str = certData->certificate.validity.notAfter.time.Data;
	if (strptime((const char *)not_after_str, "%y %m %d %H %M %S",
	    &tm_tmp) == NULL) {
		rv = KMF_ERR_VALIDITY_PERIOD;
		goto out;
	}

	errno = 0;
	if (((t_notafter = mktime(&tm_tmp)) == (time_t)(-1)) &&
	    errno == EOVERFLOW) {
		rv = KMF_ERR_VALIDITY_PERIOD;
		goto out;
	}
	*not_after = t_notafter;

out:
	if (certData != NULL) {
		kmf_free_signed_cert(certData);
		free(certData);
	}

	return (rv);
}

KMF_RETURN
kmf_set_cert_pubkey(KMF_HANDLE_T handle,
	KMF_KEY_HANDLE *KMFKey,
	KMF_X509_CERTIFICATE *Cert)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_SPKI *spki_ptr;
	KMF_PLUGIN *plugin;
	KMF_DATA KeyData = {NULL, 0};

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

	if (KMFKey == NULL || Cert == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	/* The keystore must extract the pubkey data */
	plugin = FindPlugin(handle, KMFKey->kstype);
	if (plugin != NULL && plugin->funclist->EncodePubkeyData != NULL) {
		ret = plugin->funclist->EncodePubkeyData(handle,
		    KMFKey, &KeyData);
	} else {
		return (KMF_ERR_PLUGIN_NOTFOUND);
	}

	spki_ptr = &Cert->certificate.subjectPublicKeyInfo;

	if (KeyData.Data != NULL) {
		ret = DerDecodeSPKI(&KeyData, spki_ptr);
		free(KeyData.Data);
	}

	return (ret);
}

KMF_RETURN
kmf_set_cert_subject(KMF_X509_CERTIFICATE *CertData,
	KMF_X509_NAME *subject_name_ptr)
{

	KMF_RETURN rv = KMF_OK;
	KMF_X509_NAME *temp_name_ptr = NULL;

	if (CertData != NULL && subject_name_ptr != NULL) {
		rv = CopyRDN(subject_name_ptr, &temp_name_ptr);
		if (rv == KMF_OK) {
			CertData->certificate.subject = *temp_name_ptr;
		}
	} else {
		return (KMF_ERR_BAD_PARAMETER);
	}
	return (rv);
}

KMF_RETURN
set_key_usage_extension(KMF_X509_EXTENSIONS *extns,
	int critical, uint32_t bits)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;
	BerElement *asn1 = NULL;
	BerValue *extdata;
	int bitlen, i;
	uint16_t kubits = (uint16_t)(bits & 0x0000ffff);

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

	(void) memset(&extn, 0, sizeof (extn));
	ret = copy_data(&extn.extnId, (KMF_OID *)&KMFOID_KeyUsage);
	if (ret != KMF_OK)
		return (ret);
	extn.critical = critical;
	extn.format = KMF_X509_DATAFORMAT_ENCODED;

	for (i = 7; i <= 15 && !(kubits & (1 << i)); i++)
		/* empty body */
		;

	bitlen = 16 - i;

	if ((asn1 = kmfder_alloc()) == NULL)
		return (KMF_ERR_MEMORY);

	kubits = htons(kubits);
	if (kmfber_printf(asn1, "B", (char *)&kubits, bitlen) == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}
	if (kmfber_flatten(asn1, &extdata) == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	extn.BERvalue.Data = (uchar_t *)extdata->bv_val;
	extn.BERvalue.Length = extdata->bv_len;

	free(extdata);

	ret = add_an_extension(extns, &extn);
	if (ret != KMF_OK) {
		free(extn.BERvalue.Data);
	}
out:
	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	return (ret);
}

KMF_RETURN
kmf_set_cert_ku(KMF_X509_CERTIFICATE *CertData,
	int critical, uint16_t kubits)
{
	KMF_RETURN ret = KMF_OK;

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

	ret = set_key_usage_extension(&CertData->certificate.extensions,
	    critical, kubits);

	return (ret);
}

KMF_RETURN
kmf_set_cert_issuer(KMF_X509_CERTIFICATE *CertData,
	KMF_X509_NAME *issuer_name_ptr)
{

	KMF_RETURN rv = KMF_OK;
	KMF_X509_NAME *temp_name_ptr = NULL;

	if (CertData != NULL && issuer_name_ptr != NULL) {
		rv = CopyRDN(issuer_name_ptr, &temp_name_ptr);
		if (rv == KMF_OK) {
			CertData->certificate.issuer = *temp_name_ptr;
		}
	} else {
		return (KMF_ERR_BAD_PARAMETER);
	}

	return (rv);
}

KMF_RETURN
kmf_set_cert_sig_alg(KMF_X509_CERTIFICATE *CertData,
	KMF_ALGORITHM_INDEX sigAlg)
{
	KMF_OID	*alg;

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

	alg = x509_algid_to_algoid(sigAlg);

	if (alg != NULL) {
		(void) copy_data((KMF_DATA *)
		    &CertData->certificate.signature.algorithm,
		    (KMF_DATA *)alg);
		(void) copy_data(&CertData->certificate.signature.parameters,
		    &CertData->certificate.subjectPublicKeyInfo.algorithm.
		    parameters);

		(void) copy_data(
		    &CertData->signature.algorithmIdentifier.algorithm,
		    &CertData->certificate.signature.algorithm);
		(void) copy_data(
		    &CertData->signature.algorithmIdentifier.parameters,
		    &CertData->certificate.signature.parameters);
	} else {
		return (KMF_ERR_BAD_PARAMETER);
	}

	return (KMF_OK);
}

KMF_RETURN
kmf_set_cert_validity(KMF_X509_CERTIFICATE *CertData,
	time_t notBefore, uint32_t delta)
{
	time_t		clock;
	struct tm	*gmt;
	char 		szNotBefore[256];
	char		szNotAfter[256];

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

	/* Set up validity fields */
	if (notBefore == NULL)
		clock = time(NULL);
	else
		clock = notBefore;

	gmt = gmtime(&clock);  /* valid starting today */

	/* Build the format in 2 parts so SCCS doesn't get confused */
	(void) strftime(szNotBefore, sizeof (szNotBefore),
	    "%y%m%d%H" "%M00Z", gmt);

	CertData->certificate.validity.notBefore.timeType = BER_UTCTIME;
	CertData->certificate.validity.notBefore.time.Length =
	    strlen((char *)szNotBefore);
	CertData->certificate.validity.notBefore.time.Data =
	    (uchar_t *)strdup(szNotBefore);

	clock += delta;
	gmt = gmtime(&clock);

	/* Build the format in 2 parts so SCCS doesn't get confused */
	(void) strftime(szNotAfter, sizeof (szNotAfter),
	    "%y%m%d%H" "%M00Z", gmt);

	CertData->certificate.validity.notAfter.timeType = BER_UTCTIME;
	CertData->certificate.validity.notAfter.time.Length =
	    strlen((char *)szNotAfter);
	CertData->certificate.validity.notAfter.time.Data =
	    (uchar_t *)strdup(szNotAfter);

	return (KMF_OK);
}

/*
 * Utility routine to set Integer values in the Certificate template
 * for things like serialNumber and Version. The data structure
 * expects pointers, not literal values, so we must allocate
 * and copy here.  Don't use memory from the stack since this data
 * is freed later and that would be bad.
 */
KMF_RETURN
set_integer(KMF_DATA *data, void *value, int length)
{
	if (data == NULL || value == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	data->Data = malloc(length);
	if (data->Data == NULL)
		return (KMF_ERR_MEMORY);

	data->Length = length;
	(void) memcpy((void *)data->Data, (const void *)value, length);

	return (KMF_OK);
}

static KMF_RETURN
set_bigint(KMF_BIGINT *data, KMF_BIGINT *bigint)
{
	if (data == NULL || bigint == NULL || bigint->len == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	data->val = malloc(bigint->len);
	if (data->val == NULL)
		return (KMF_ERR_MEMORY);

	data->len = bigint->len;

	(void) memcpy((void *)data->val, bigint->val, bigint->len);

	return (KMF_OK);

}

KMF_RETURN
kmf_set_cert_serial(KMF_X509_CERTIFICATE *CertData,
	KMF_BIGINT *serno)
{
	if (CertData == NULL || serno == NULL || serno->len == 0)
		return (KMF_ERR_BAD_PARAMETER);
	return (set_bigint(&CertData->certificate.serialNumber, serno));
}

KMF_RETURN
kmf_set_cert_version(KMF_X509_CERTIFICATE *CertData,
	uint32_t version)
{
	if (CertData == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	/*
	 * From RFC 3280:
	 * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
	 */
	if (version != 0 && version != 1 && version != 2)
		return (KMF_ERR_BAD_PARAMETER);
	return (set_integer(&CertData->certificate.version, (void *)&version,
	    sizeof (uint32_t)));
}

KMF_RETURN
kmf_set_cert_issuer_altname(KMF_X509_CERTIFICATE *CertData,
	int critical,
	KMF_GENERALNAMECHOICES nametype,
	char *namedata)
{
	if (CertData == NULL || namedata == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	return (kmf_set_altname(&CertData->certificate.extensions,
	    (KMF_OID *)&KMFOID_IssuerAltName, critical, nametype, namedata));
}

KMF_RETURN
kmf_set_cert_subject_altname(KMF_X509_CERTIFICATE *CertData,
	int critical,
	KMF_GENERALNAMECHOICES nametype,
	char *namedata)
{
	if (CertData == NULL || namedata == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	return (kmf_set_altname(&CertData->certificate.extensions,
	    (KMF_OID *)&KMFOID_SubjectAltName, critical, nametype, namedata));
}

KMF_RETURN
kmf_add_cert_eku(KMF_X509_CERTIFICATE *CertData, KMF_OID *ekuOID,
	int critical)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION *foundextn;
	KMF_X509_EXTENSION newextn;
	BerElement *asn1 = NULL;
	BerValue *extdata = NULL;
	char *olddata = NULL;
	size_t oldsize = 0;
	KMF_X509EXT_EKU ekudata;

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

	(void) memset(&ekudata, 0, sizeof (KMF_X509EXT_EKU));
	(void) memset(&newextn, 0, sizeof (newextn));

	foundextn = FindExtn(&CertData->certificate.extensions,
	    (KMF_OID *)&KMFOID_ExtendedKeyUsage);
	if (foundextn != NULL) {
		ret = GetSequenceContents((char *)foundextn->BERvalue.Data,
		    foundextn->BERvalue.Length,	&olddata, &oldsize);
		if (ret != KMF_OK)
			goto out;

		/*
		 * If the EKU is already in the cert, then just return OK.
		 */
		ret = parse_eku_data(&foundextn->BERvalue, &ekudata);
		if (ret == KMF_OK) {
			if (isEKUPresent(&ekudata, ekuOID)) {
				goto out;
			}
		}
	}
	if ((asn1 = kmfder_alloc()) == NULL)
		return (KMF_ERR_MEMORY);

	if (kmfber_printf(asn1, "{") == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	/* Write the old extension data first */
	if (olddata != NULL && oldsize > 0) {
		if (kmfber_write(asn1, olddata, oldsize, 0) == -1) {
			ret = KMF_ERR_ENCODING;
			goto out;
		}
	}

	/* Append this EKU OID and close the sequence */
	if (kmfber_printf(asn1, "D}", ekuOID) == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	if (kmfber_flatten(asn1, &extdata) == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	/*
	 * If we are just adding to an existing list of EKU OIDs,
	 * just replace the BER data associated with the found extension.
	 */
	if (foundextn != NULL) {
		free(foundextn->BERvalue.Data);
		foundextn->critical = critical;
		foundextn->BERvalue.Data = (uchar_t *)extdata->bv_val;
		foundextn->BERvalue.Length = extdata->bv_len;
	} else {
		ret = copy_data(&newextn.extnId,
		    (KMF_DATA *)&KMFOID_ExtendedKeyUsage);
		if (ret != KMF_OK)
			goto out;
		newextn.critical = critical;
		newextn.format = KMF_X509_DATAFORMAT_ENCODED;
		newextn.BERvalue.Data = (uchar_t *)extdata->bv_val;
		newextn.BERvalue.Length = extdata->bv_len;
		ret = kmf_set_cert_extn(CertData, &newextn);
		if (ret != KMF_OK)
			free(newextn.BERvalue.Data);
	}

out:
	kmf_free_eku(&ekudata);
	if (extdata != NULL)
		free(extdata);

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

	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	if (ret != KMF_OK)
		kmf_free_data(&newextn.extnId);

	return (ret);
}

KMF_RETURN
kmf_set_cert_extn(KMF_X509_CERTIFICATE *CertData,
	KMF_X509_EXTENSION *extn)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSIONS *exts;

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

	exts = &CertData->certificate.extensions;

	ret = add_an_extension(exts, extn);

	return (ret);
}

KMF_RETURN
kmf_set_cert_basic_constraint(KMF_X509_CERTIFICATE *CertData,
	KMF_BOOL critical, KMF_X509EXT_BASICCONSTRAINTS *constraint)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_EXTENSION extn;
	BerElement *asn1 = NULL;
	BerValue *extdata;

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

	(void) memset(&extn, 0, sizeof (extn));
	ret = copy_data(&extn.extnId, (KMF_OID *)&KMFOID_BasicConstraints);
	if (ret != KMF_OK)
		return (ret);
	extn.critical = critical;
	extn.format = KMF_X509_DATAFORMAT_ENCODED;

	if ((asn1 = kmfder_alloc()) == NULL)
		return (KMF_ERR_MEMORY);

	if (kmfber_printf(asn1, "{") == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	if (kmfber_printf(asn1, "b", constraint->cA) == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	if (constraint->pathLenConstraintPresent) {
		/* Write the pathLenConstraint value */
		if (kmfber_printf(asn1, "i",
		    constraint->pathLenConstraint) == -1) {
			ret = KMF_ERR_ENCODING;
			goto out;
		}
	}

	if (kmfber_printf(asn1, "}") == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	if (kmfber_flatten(asn1, &extdata) == -1) {
		ret = KMF_ERR_ENCODING;
		goto out;
	}

	extn.BERvalue.Data = (uchar_t *)extdata->bv_val;
	extn.BERvalue.Length = extdata->bv_len;

	free(extdata);
	ret = kmf_set_cert_extn(CertData, &extn);
	if (ret != KMF_OK) {
		free(extn.BERvalue.Data);
	}

out:
	if (asn1 != NULL)
		kmfber_free(asn1, 1);

	return (ret);
}


/*
 * Phase 1 APIs still needed to maintain compat with elfsign.
 */
KMF_RETURN
KMF_GetCertSubjectNameString(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	return (kmf_get_cert_subject_str(handle, SignedCert, result));
}

KMF_RETURN
KMF_GetCertIssuerNameString(KMF_HANDLE_T handle, const KMF_DATA *SignedCert,
    char **result)
{
	return (kmf_get_cert_issuer_str(handle, SignedCert, result));
}

KMF_RETURN
KMF_GetCertIDString(const KMF_DATA *SignedCert,	char **idstr)
{
	return (kmf_get_cert_id_str(SignedCert, idstr));
}