/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*
 * Copyright (c) 1995-1999 Intel Corporation. All rights reserved.
 */

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

#include <strings.h>
#include <kmftypes.h>
#include <ber_der.h>
#include <kmfapi.h>
#include <kmfapiP.h>

#include <stdio.h>

#define	DSA_RAW_SIG_LEN	40

static uint8_t OID_ExtensionRequest[] = { OID_PKCS_9, 14 };
const KMF_OID extension_request_oid = {OID_PKCS_9_LENGTH + 1,
	OID_ExtensionRequest};

static KMF_RETURN
encode_algoid(BerElement *asn1, KMF_X509_ALGORITHM_IDENTIFIER *algoid)
{
	KMF_RETURN ret = KMF_OK;

	if (kmfber_printf(asn1, "{D", &algoid->algorithm) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
	}
	if (algoid->parameters.Data == NULL ||
	    algoid->parameters.Length == 0) {
		if (kmfber_printf(asn1, "n}") == -1)
			return (KMF_ERR_BAD_CERT_FORMAT);
	} else {
		/*
		 * The algorithm data can be anything, so we just write it
		 * straight into the buffer.  It is already DER encoded.
		 */
		(void) kmfber_write(asn1, (char *)algoid->parameters.Data,
		    algoid->parameters.Length, 0);
		if (kmfber_printf(asn1, "}") == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
		}
	}

	return (ret);
}

static void
free_data(KMF_DATA *data)
{
	if (data == NULL || data->Data == NULL)
		return;

	free(data->Data);
	data->Data = NULL;
	data->Length = 0;
}

static void
free_algoid(KMF_X509_ALGORITHM_IDENTIFIER *algoid)
{
	free_data(&algoid->algorithm);
	free_data(&algoid->parameters);
}

static void
free_decoded_spki(KMF_X509_SPKI *spki)
{
	if (spki != NULL) {
		free_algoid(&spki->algorithm);
		free_data(&spki->subjectPublicKey);
	}
}

static void
free_rdn_data(KMF_X509_NAME *name)
{
	KMF_X509_RDN 		*newrdn = NULL;
	KMF_X509_TYPE_VALUE_PAIR *av = NULL;
	int i, j;

	if (name && name->numberOfRDNs) {
		for (i = 0; i < name->numberOfRDNs; i++) {
			newrdn = &name->RelativeDistinguishedName[i];
			for (j = 0; j < newrdn->numberOfPairs; j++) {
				av = &newrdn->AttributeTypeAndValue[j];
				free_data(&av->type);
				free_data(&av->value);
			}
			free(newrdn->AttributeTypeAndValue);
		}
		free(name->RelativeDistinguishedName);
		name->numberOfRDNs = 0;
		name->RelativeDistinguishedName = NULL;
	}
}

static void
free_validity(KMF_X509_VALIDITY *validity)
{
	free_data(&validity->notBefore.time);
	free_data(&validity->notAfter.time);
}

static void
free_one_extension(KMF_X509_EXTENSION *exptr)
{
	free_data(&exptr->extnId);
	free_data(&exptr->BERvalue);

	if (exptr->value.tagAndValue) {
		free_data(&exptr->value.tagAndValue->value);
		free(exptr->value.tagAndValue);
	}
}

static void
free_extensions(KMF_X509_EXTENSIONS *extns)
{
	int i;
	KMF_X509_EXTENSION *exptr;

	if (extns && extns->numberOfExtensions > 0) {
		for (i = 0; i < extns->numberOfExtensions; i++) {
			exptr = &extns->extensions[i];
			free_one_extension(exptr);
		}
		free(extns->extensions);
		extns->numberOfExtensions = 0;
		extns->extensions = NULL;
	}
}

static void
free_tbscsr(KMF_TBS_CSR *tbscsr)
{
	if (tbscsr) {
		free_data(&tbscsr->version);

		free_rdn_data(&tbscsr->subject);

		free_decoded_spki(&tbscsr->subjectPublicKeyInfo);

		free_extensions(&tbscsr->extensions);
	}
}


static void
free_bigint(KMF_BIGINT *bn)
{
	if (bn != NULL && bn->val != NULL) {
		free(bn->val);
		bn->val = NULL;
		bn->len = 0;
	}
}

static void
free_tbscert(KMF_X509_TBS_CERT *tbscert)
{
	if (tbscert) {
		free_data(&tbscert->version);
		free_bigint(&tbscert->serialNumber);
		free_algoid(&tbscert->signature);

		free_rdn_data(&tbscert->issuer);
		free_rdn_data(&tbscert->subject);

		free_validity(&tbscert->validity);

		free_data(&tbscert->issuerUniqueIdentifier);
		free_data(&tbscert->subjectUniqueIdentifier);
		free_decoded_spki(&tbscert->subjectPublicKeyInfo);
		free_extensions(&tbscert->extensions);

		free_data(&tbscert->issuerUniqueIdentifier);
		free_data(&tbscert->subjectUniqueIdentifier);
	}
}

static void
free_decoded_cert(KMF_X509_CERTIFICATE *certptr)
{
	if (!certptr)
		return;

	free_tbscert(&certptr->certificate);

	free_algoid(&certptr->signature.algorithmIdentifier);
	free_data(&certptr->signature.encrypted);
}

static KMF_RETURN
get_algoid(BerElement *asn1, KMF_X509_ALGORITHM_IDENTIFIER *algoid)
{
	KMF_RETURN ret = KMF_OK;
	ber_tag_t tag, newtag;
	ber_len_t size;
	BerValue AlgOID = {NULL, 0};

	tag = kmfber_next_element(asn1, &size, NULL);
	if (tag != BER_CONSTRUCTED_SEQUENCE)
		return (KMF_ERR_BAD_CERT_FORMAT);

	if ((tag = kmfber_scanf(asn1, "{Dt", &AlgOID, &newtag)) == -1) {
		return (KMF_ERR_BAD_CERT_FORMAT);
	}
	algoid->algorithm.Data = (uchar_t *)AlgOID.bv_val;
	algoid->algorithm.Length = AlgOID.bv_len;

	if (newtag == BER_NULL) {
		(void) kmfber_scanf(asn1, "n}");
		algoid->parameters.Data = NULL;
		algoid->parameters.Length = 0;
	} else {
		/* Peek at the tag and length bytes */
		if ((kmfber_scanf(asn1, "tl", &tag, &size)) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}

		/*
		 * We need to read the tag and the length bytes too,
		 * so adjust the size.
		 */
		size += kmfber_calc_taglen(tag) + kmfber_calc_lenlen(size);
		algoid->parameters.Data = malloc(size);
		if (algoid->parameters.Data == NULL) {
			ret = KMF_ERR_MEMORY;
			goto cleanup;
		}
		/* read the raw data into the Algoritm params area. */
		if (kmfber_read(asn1, (char *)algoid->parameters.Data,
		    size) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
		algoid->parameters.Length = size;
		if ((tag = kmfber_scanf(asn1, "}")) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
		}
	}
cleanup:
	if (ret != KMF_OK) {
		free_algoid(algoid);
	}

	return (ret);
}

static KMF_RETURN
CopyData(KMF_DATA *src, KMF_DATA *dst)
{
	if (src && dst && src->Data != NULL && src->Length > 0) {
		dst->Length = src->Length;
		dst->Data = malloc(dst->Length);
		if (dst->Data == NULL)
			return (KMF_ERR_MEMORY);
		(void) memcpy(dst->Data, src->Data, src->Length);
	}
	return (KMF_OK);
}

static KMF_RETURN
encode_spki(BerElement *asn1, KMF_X509_SPKI *spki)
{
	KMF_RETURN ret = KMF_OK;

	if (kmfber_printf(asn1, "{") == -1)
		return (KMF_ERR_BAD_CERT_FORMAT);

	if ((ret = encode_algoid(asn1, &spki->algorithm)) != KMF_OK)
		return (ret);

	if (kmfber_printf(asn1, "B}", spki->subjectPublicKey.Data,
	    spki->subjectPublicKey.Length * 8) == -1)
		return (KMF_ERR_BAD_CERT_FORMAT);

	return (ret);
}

KMF_RETURN
DerEncodeSPKI(KMF_X509_SPKI *spki, KMF_DATA *EncodedSPKI)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1;
	BerValue *result;

	if (spki == NULL || EncodedSPKI == NULL)
		return (KMF_ERR_BAD_PARAMETER);

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

	if ((ret = encode_spki(asn1, spki)) != KMF_OK) {
		return (ret);
	}

	if (kmfber_flatten(asn1, &result) == -1) {
		kmfber_free(asn1, 1);
		return (KMF_ERR_ENCODING);
	}

	EncodedSPKI->Data = (uchar_t *)result->bv_val;
	EncodedSPKI->Length = result->bv_len;

	free(result);
	kmfber_free(asn1, 1);
	return (KMF_OK);
}

static KMF_RETURN
get_spki(BerElement *asn1, KMF_X509_SPKI *spki)
{
	KMF_RETURN ret = KMF_OK;
	char *bitstr = NULL;
	ber_len_t size;

	if (kmfber_scanf(asn1, "{") == -1)
		return (KMF_ERR_BAD_CERT_FORMAT);

	if ((ret = get_algoid(asn1, &spki->algorithm)) != KMF_OK)
		return (ret);

	if (kmfber_scanf(asn1, "B}", &bitstr, &size) == BER_BIT_STRING) {
		spki->subjectPublicKey.Data = (uchar_t *)bitstr;
		spki->subjectPublicKey.Length = size / 8;
	} else {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}
cleanup:
	if (ret != KMF_OK) {
		if (bitstr != NULL)
			free(bitstr);
		spki->subjectPublicKey.Data = NULL;
		spki->subjectPublicKey.Length = 0;

		free_algoid(&spki->algorithm);
	}
	return (ret);
}

KMF_RETURN
DerEncodeDSASignature(KMF_DATA *rawdata, KMF_DATA *signature)
{
	BerElement *asn1;
	BerValue *buf;
	int n;

	if (rawdata == NULL || signature == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (rawdata->Data == NULL || rawdata->Length != DSA_RAW_SIG_LEN)
		return (KMF_ERR_BAD_PARAMETER);

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

	/*
	 * The DSA signature is the concatenation of 2 SHA-1 hashed
	 * bignum values.
	 */
	n = DSA_RAW_SIG_LEN/2;
	if (kmfber_printf(asn1, "{II}",
	    rawdata->Data, n, &rawdata->Data[n], n) == -1) {
		kmfber_free(asn1, 1);
		return (KMF_ERR_MEMORY);
	}

	if (kmfber_flatten(asn1, &buf) == -1) {
		kmfber_free(asn1, 1);
		return (KMF_ERR_ENCODING);
	}

	signature->Data = (uchar_t *)buf->bv_val;
	signature->Length = buf->bv_len;

	kmfber_free(asn1, 1);
	free(buf);

	return (KMF_OK);
}

KMF_RETURN
DerDecodeDSASignature(KMF_DATA *encoded, KMF_DATA *signature)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue buf, *R = NULL, *S = NULL;

	buf.bv_val = (char *)encoded->Data;
	buf.bv_len = encoded->Length;

	if (encoded == NULL || encoded->Data == NULL ||
	    signature == NULL)
		return (KMF_ERR_BAD_PARAMETER);

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

	if ((asn1 = kmfder_init(&buf)) == NULL)
		return (KMF_ERR_MEMORY);

	if (kmfber_scanf(asn1, "{II}", &R, &S) == -1) {
		ret = KMF_ERR_BAD_PARAMETER;
		goto cleanup;
	}
	signature->Length = R->bv_len + S->bv_len;
	signature->Data = malloc(signature->Length);
	if (signature->Data == NULL)  {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	(void) memcpy(signature->Data, R->bv_val, R->bv_len);
	(void) memcpy(&signature->Data[R->bv_len], S->bv_val, S->bv_len);

cleanup:
	if (R && R->bv_val)
		free(R->bv_val);
	if (S && S->bv_val)
		free(S->bv_val);

	if (S) free(S);
	if (R) free(R);

	if (asn1) kmfber_free(asn1, 1);

	return (ret);
}

KMF_RETURN
DerDecodeSPKI(KMF_DATA *EncodedSPKI, KMF_X509_SPKI *spki)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1;
	BerValue bv;

	if (EncodedSPKI == NULL || EncodedSPKI->Data == NULL ||
	    spki == NULL)
		return (KMF_ERR_BAD_PARAMETER);

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

	bv.bv_val = (char *)EncodedSPKI->Data;
	bv.bv_len = EncodedSPKI->Length;

	if ((asn1 = kmfder_init(&bv)) == NULL)
		return (KMF_ERR_MEMORY);

	ret = get_spki(asn1, spki);

cleanup:
	if (ret != KMF_OK) {
		free_decoded_spki(spki);
	}
	kmfber_free(asn1, 1);

	return (ret);
}

KMF_RETURN
CopySPKI(KMF_X509_SPKI *src,
		KMF_X509_SPKI **dest)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_SPKI *newspki;

	*dest = NULL;

	newspki = malloc(sizeof (KMF_X509_SPKI));
	if (newspki == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(newspki, 0, sizeof (KMF_X509_SPKI));

	ret = CopyData(&src->algorithm.algorithm,
	    &newspki->algorithm.algorithm);
	if (ret != KMF_OK)
		goto cleanup;

	ret = CopyData(&src->algorithm.parameters,
	    &newspki->algorithm.parameters);
	if (ret != KMF_OK)
		goto cleanup;

	ret = CopyData(&src->subjectPublicKey,
	    &newspki->subjectPublicKey);
	if (ret != KMF_OK)
		goto cleanup;

	*dest = newspki;
cleanup:
	if (ret != KMF_OK) {
		if (newspki)
			free_decoded_spki(newspki);
	}
	return (ret);
}

static KMF_RETURN
encode_validity(BerElement *asn1, KMF_X509_VALIDITY *validity)
{
	int ret;

	ret = kmfber_printf(asn1, "{tsts}",
	    validity->notBefore.timeType,
	    validity->notBefore.time.Data,
	    validity->notAfter.timeType,
	    validity->notAfter.time.Data);

	if (ret == -1)
		return (KMF_ERR_BAD_CERT_FORMAT);

	return (KMF_OK);
}

static KMF_RETURN
get_validity(BerElement *asn1, KMF_X509_VALIDITY *validity)
{
	KMF_RETURN ret = KMF_OK;
	int tag;
	int t1, t2;
	ber_len_t size;
	char *t1str, *t2str;

	(void) memset(validity, 0, sizeof (KMF_X509_VALIDITY));

	tag = kmfber_next_element(asn1, &size, NULL);
	if (tag != BER_CONSTRUCTED_SEQUENCE) {
		return (KMF_ERR_BAD_CERT_FORMAT);
	}

	if (kmfber_scanf(asn1, "{tata}", &t1, &t1str, &t2, &t2str) == -1) {
		return (KMF_ERR_BAD_CERT_FORMAT);
	}

	validity->notBefore.timeType = t1;
	validity->notBefore.time.Data = (uchar_t *)t1str;
	validity->notBefore.time.Length = strlen(t1str);

	validity->notAfter.timeType = t2;
	validity->notAfter.time.Data = (uchar_t *)t2str;
	validity->notAfter.time.Length = strlen(t2str);

	return (ret);
}

KMF_RETURN
AddRDN(KMF_X509_NAME *name, KMF_X509_RDN *newrdn)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_RDN *rdnslot = NULL;

	/* Add new RDN record to existing list */
	name->numberOfRDNs++;
	name->RelativeDistinguishedName =
	    realloc(name->RelativeDistinguishedName,
	    name->numberOfRDNs * sizeof (KMF_X509_RDN));

	if (name->RelativeDistinguishedName == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	rdnslot = &name->RelativeDistinguishedName[name->numberOfRDNs-1];

	if (newrdn) {
		(void) memcpy(rdnslot, newrdn, sizeof (KMF_X509_RDN));
	} else {
		rdnslot->numberOfPairs = 0;
		rdnslot->AttributeTypeAndValue = NULL;
	}

cleanup:
	/* No cleanup needed here */
	return (ret);
}

static KMF_RETURN
encode_rdn(BerElement *asn1, KMF_X509_NAME *name)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_TYPE_VALUE_PAIR *attrtvpair = NULL;
	int i;
	KMF_X509_RDN *rdn;

	if (kmfber_printf(asn1, "{") == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	for (i = 0; i < name->numberOfRDNs; i++) {
		if (kmfber_printf(asn1, "[") == -1) {
			ret = KMF_ERR_MEMORY;
			goto cleanup;
		}
		rdn = &name->RelativeDistinguishedName[i];
		attrtvpair = rdn->AttributeTypeAndValue;

		if (rdn->numberOfPairs > 0) {
			if (kmfber_printf(asn1, "{Dto}",
			    &attrtvpair->type,
			    attrtvpair->valueType,
			    attrtvpair->value.Data,
			    attrtvpair->value.Length) == -1) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}
		}
		if (kmfber_printf(asn1, "]") == -1) {
			ret = KMF_ERR_MEMORY;
			goto cleanup;
		}
	}

	if (kmfber_printf(asn1, "}") == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

cleanup:
	/* No cleanup needed here */

	return (ret);
}


KMF_RETURN
CopyRDN(KMF_X509_NAME *srcname, KMF_X509_NAME **destname)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_NAME 		*newname = NULL;
	KMF_X509_RDN 		*rdn, *dstrdn;
	KMF_X509_TYPE_VALUE_PAIR *av = NULL;
	KMF_X509_TYPE_VALUE_PAIR *srcav = NULL;
	KMF_X509_TYPE_VALUE_PAIR *dstav = NULL;
	int i, j;

	newname = malloc(sizeof (KMF_X509_NAME));
	if (newname == NULL)
		return (KMF_ERR_MEMORY);
	(void) memset(newname, 0, sizeof (KMF_X509_NAME));

	newname->numberOfRDNs = srcname->numberOfRDNs;
	newname->RelativeDistinguishedName = malloc(newname->numberOfRDNs *
	    sizeof (KMF_X509_RDN));
	if (newname->RelativeDistinguishedName == NULL) {
		free(newname);
		return (KMF_ERR_MEMORY);
	}
	/* Copy each RDN in the list */
	for (i = 0; i < newname->numberOfRDNs; i++) {
		rdn = &srcname->RelativeDistinguishedName[i];

		dstrdn = &newname->RelativeDistinguishedName[i];
		(void) memset(dstrdn, 0, sizeof (KMF_X509_RDN));

		dstrdn->numberOfPairs = rdn->numberOfPairs;
		if (dstrdn->numberOfPairs > 0) {
			av = malloc(dstrdn->numberOfPairs *
			    sizeof (KMF_X509_TYPE_VALUE_PAIR));
			if (av == NULL) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}
			(void) memset(av, 0, dstrdn->numberOfPairs *
			    sizeof (KMF_X509_TYPE_VALUE_PAIR));

			dstrdn->AttributeTypeAndValue = av;
			if (av == NULL) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}
			/* Copy each A/V pair in the list */
			for (j = 0; j < dstrdn->numberOfPairs; j++) {
				srcav = &rdn->AttributeTypeAndValue[j];
				dstav = &dstrdn->AttributeTypeAndValue[j];
				if ((ret = CopyData(&srcav->type,
				    &dstav->type)) != KMF_OK)
					goto cleanup;
				dstav->valueType = srcav->valueType;
				if ((ret = CopyData(&srcav->value,
				    &dstav->value)) != KMF_OK)
					goto cleanup;
			}
		} else {
			dstrdn->AttributeTypeAndValue = NULL;
		}
	}
	*destname = newname;

cleanup:
	if (ret != KMF_OK) {
		if (newname)
			free_rdn_data(newname);

		free(newname);
		*destname = NULL;
	}
	return (ret);
}

#define	VALID_DIRECTORYSTRING_TAG(t) ( \
	(t == BER_UTF8_STRING) || \
	(t == BER_PRINTABLE_STRING) || \
	(t == BER_IA5STRING) || \
	(t == BER_T61STRING) || \
	(t == BER_BMP_STRING) || \
	(t == BER_UNIVERSAL_STRING))

static KMF_RETURN
get_rdn(BerElement *asn1, KMF_X509_NAME *name)
{
	KMF_RETURN ret = KMF_OK;
	ber_len_t size;
	char *end;
	int tag;
	BerValue AttrOID;
	char *AttrValue = NULL;
	KMF_X509_TYPE_VALUE_PAIR *newpair = NULL;
	KMF_X509_RDN 		newrdn;

	/*
	 * AttributeType	::=  OBJECT IDENTIFIER
	 * AttributeValue	::=  ANY
	 *
	 * AttributeTypeAndValue	::=  SEQUENCE {
	 *	type    AttributeType,
	 *	value   AttributeValue }
	 *
	 * Name ::= CHOICE { -- only one possibility for now --
	 * 		rdnSequence  RDNSequence }
	 *
	 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
	 *
	 * DistinguishedName ::=   RDNSequence
	 *
	 * RelativeDistinguishedName  ::=
	 *		 SET SIZE (1 .. MAX) OF AttributeTypeAndValue
	 *
	 */

	name->numberOfRDNs = 0;
	name->RelativeDistinguishedName = NULL;

	/* Get the beginning of the RDN Set and a ptr to the end */
	tag = kmfber_first_element(asn1, &size, &end);
	if (tag != BER_CONSTRUCTED_SET) {
		goto cleanup;
	}

	/* Walk through the individual SET items until the "end" is reached */
	while ((tag = kmfber_next_element(asn1, &size, end)) ==
	    BER_CONSTRUCTED_SET) {
		/* Skip over the SET tag */
		if (kmfber_scanf(asn1, "T", &tag) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			break;
		}

		/* An "empty" set member means we tack on an empty node */
		if (size == 0) {
			if ((ret = AddRDN(name, NULL)) != KMF_OK)
				goto cleanup;
			continue;
		}

		/* Attr OID and peek at the next tag and field length */
		if (kmfber_scanf(asn1, "{Dtl", &AttrOID, &tag, &size) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			break;
		}

		if (!(VALID_DIRECTORYSTRING_TAG(tag))) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			break;
		}

		if (kmfber_scanf(asn1, "a}]", &AttrValue) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			break;
		}

		/* Allocate a new name/value pair record */
		newpair = malloc(sizeof (KMF_X509_TYPE_VALUE_PAIR));
		if (newpair == NULL) {
			ret = KMF_ERR_MEMORY;
			break;
		}
		(void) memset(newpair, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR));
		newpair->type.Data = (uchar_t *)AttrOID.bv_val;
		newpair->type.Length = AttrOID.bv_len;
		newpair->valueType = tag; /* what kind of string is it? */
		newpair->value.Data = (uchar_t *)AttrValue;
		newpair->value.Length = strlen(AttrValue);

		(void) memset(&newrdn, 0, sizeof (KMF_X509_RDN));
		newrdn.numberOfPairs = 1;
		newrdn.AttributeTypeAndValue = newpair;

		if ((ret = AddRDN(name, &newrdn)) != KMF_OK)
			break;
	}

cleanup:
	if (ret != KMF_OK) {
		free_rdn_data(name);
	}
	return (ret);
}

static KMF_RETURN
set_der_integer(KMF_DATA *data, int value)
{
	if (data == NULL)
		return (KMF_ERR_BAD_PARAMETER);

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

	data->Length = sizeof (int);
	(void) memcpy((void *)data->Data, (const void *)&value, sizeof (int));

	return (KMF_OK);
}

static KMF_RETURN
set_bigint(KMF_BIGINT *data, KMF_BIGINT *bigint)
{
	if (data == NULL || bigint == 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, (const void *)bigint->val,
	    bigint->len);

	return (KMF_OK);
}

static KMF_RETURN
encode_uniqueid(BerElement *asn1, int tag, KMF_DATA *id)
{
	KMF_RETURN ret = KMF_OK;
	uint32_t len;

	len = kmfber_calc_taglen(BER_BIT_STRING) +
	    kmfber_calc_lenlen(id->Length * 8) + id->Length;
	if (kmfber_printf(asn1, "TlB", tag, len,
	    id->Data, id->Length * 8) == -1)
		return (KMF_ERR_BAD_CERT_FORMAT);

	return (ret);
}

static KMF_RETURN
encode_extension_list(BerElement *asn1, KMF_X509_EXTENSIONS *extns)
{
	KMF_RETURN ret = KMF_OK;
	int i;

	for (i = 0; i < extns->numberOfExtensions; i++) {
		BerValue v;
		v.bv_val = (char *)extns->extensions[i].extnId.Data;
		v.bv_len = extns->extensions[i].extnId.Length;

		if (kmfber_printf(asn1, "{D", &v) == -1)  {
			ret = KMF_ERR_ENCODING;
			goto cleanup;
		}

		if (extns->extensions[i].critical) {
			if (kmfber_printf(asn1, "b",
			    extns->extensions[i].critical) == -1) {
				ret = KMF_ERR_ENCODING;
				goto cleanup;
			}
		}

		if (kmfber_printf(asn1, "o}",
		    extns->extensions[i].BERvalue.Data,
		    extns->extensions[i].BERvalue.Length) == -1) {
			ret = KMF_ERR_ENCODING;
			goto cleanup;
		}
	}
cleanup:
	return (ret);
}

static KMF_RETURN
encode_extensions(BerElement *asn1, KMF_X509_EXTENSIONS *extns)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *extn = NULL;
	BerValue *extnvalue = NULL;

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

	if (kmfber_printf(extn, "{") == -1) {
		ret = KMF_ERR_ENCODING;
		goto cleanup;
	}

	ret = encode_extension_list(extn, extns);

	if (kmfber_printf(extn, "}") == -1) {
		ret = KMF_ERR_ENCODING;
		goto cleanup;
	}

	if (kmfber_flatten(extn, &extnvalue) == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	if (kmfber_printf(asn1, "Tl", 0xA3, extnvalue->bv_len) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if (kmfber_write(asn1, extnvalue->bv_val, extnvalue->bv_len, 0) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

cleanup:
	kmfber_free(extn, 1);
	if (extnvalue != NULL)
		kmfber_bvfree(extnvalue);

	return (ret);
}

static KMF_RETURN
get_one_extension(BerElement *asn1, KMF_X509_EXTENSION **retex, char *end)
{
	KMF_RETURN ret = KMF_OK;
	ber_len_t size;
	int  critical, tag;
	KMF_X509_EXTENSION *ex = NULL;
	BerValue extOID;
	BerValue extValue;
	BerElement *extnber = NULL;

	if (kmfber_scanf(asn1, "T", &tag) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	tag = kmfber_next_element(asn1, &size, end);
	if (tag != BER_OBJECT_IDENTIFIER) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}
	if (kmfber_scanf(asn1, "D", &extOID) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	tag = kmfber_next_element(asn1, &size, end);
	if (tag != BER_BOOLEAN) {
		critical = 0;
		if (tag != BER_OCTET_STRING)
			goto cleanup;
	} else {
		if (kmfber_scanf(asn1, "b", &critical) == -1)
			goto cleanup;
	}

	tag = kmfber_next_element(asn1, &size, end);
	if (tag != BER_OCTET_STRING)  {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}
	if (kmfber_scanf(asn1, "o", &extValue) == -1)  {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	/* allocate a new Extension record */
	ex = malloc(sizeof (KMF_X509_EXTENSION));
	if (ex == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	(void) memset(ex, 0, sizeof (ex));

	ex->extnId.Data = (uchar_t *)extOID.bv_val;
	ex->extnId.Length = extOID.bv_len;
	ex->critical = critical;
	ex->format = KMF_X509_DATAFORMAT_ENCODED;
	ex->BERvalue.Data = (uchar_t *)extValue.bv_val;
	ex->BERvalue.Length = extValue.bv_len;

	/* Tag and value is a little tricky */
	ex->value.tagAndValue = malloc(sizeof (KMF_X509EXT_TAGandVALUE));
	if (ex->value.tagAndValue == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	(void) memset(ex->value.tagAndValue, 0,
	    sizeof (KMF_X509EXT_TAGandVALUE));

	/* Parse the Extension value field */
	extnber = kmfder_init(&extValue);
	if (extnber == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	/* Get the tag and length of the extension field */
	if (kmfber_scanf(extnber, "tl", &tag, &size) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if (kmfber_scanf(extnber, "T", &tag) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	ex->value.tagAndValue->value.Data = malloc(size);
	ex->value.tagAndValue->value.Length = size;
	size = kmfber_read(extnber,
	    (char *)ex->value.tagAndValue->value.Data, size);
	if (size != ex->value.tagAndValue->value.Length) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}
	kmfber_free(extnber, 1);
	ex->value.tagAndValue->type = tag;

	*retex = ex;
cleanup:
	if (ret != KMF_OK) {
		if (ex != NULL)
			free_one_extension(ex);
	}

	return (ret);
}

static KMF_RETURN
get_extensions(BerElement *asn1, KMF_X509_EXTENSIONS *extns)
{
	KMF_RETURN ret = KMF_OK;
	ber_len_t size;
	char *end = NULL;
	KMF_X509_EXTENSION *ex = NULL;

	/*
	 * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
	 *
	 * Extension  ::=  SEQUENCE  {
	 *	extnID		OBJECT IDENTIFIER,
	 *	critical	BOOLEAN DEFAULT FALSE,
	 *	extnValue	OCTET STRING  }
	 *
	 * { {{D}Bo}, ... }
	 */
	if (kmfber_first_element(asn1, &size, &end) !=
	    BER_CONSTRUCTED_SEQUENCE)
		return (KMF_ERR_BAD_CERT_FORMAT);

	while (kmfber_next_element(asn1, &size, end) ==
	    BER_CONSTRUCTED_SEQUENCE) {
		ret = get_one_extension(asn1, &ex, end);
		if (ret != KMF_OK)
			goto cleanup;

		extns->numberOfExtensions++;
		extns->extensions = realloc(extns->extensions,
		    extns->numberOfExtensions *
		    sizeof (KMF_X509_EXTENSION));
		if (extns->extensions == NULL) {
			ret = KMF_ERR_MEMORY;
			break;
		}

		extns->extensions[extns->numberOfExtensions-1] = *ex;
		free(ex);
	}

cleanup:
	if (ret != KMF_OK)
		free_extensions(extns);

	return (ret);
}

KMF_RETURN
decode_tbscert_data(BerElement *asn1,
	KMF_X509_TBS_CERT **signed_cert_ptr_ptr)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_TBS_CERT	*tbscert = NULL;
	int tag, version;
	struct berval *bvserno = NULL;
	KMF_BIGINT serno;

	if (kmfber_scanf(asn1, "{t", &tag) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	/* Version number is optional */
	if (tag == 0xA0) {
		if (kmfber_scanf(asn1, "Ti", &tag, &version) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
	} else {
		version = 0; /* DEFAULT v1 (0) */
	}

	/* Now get the serial number, it is not optional */
	if (kmfber_scanf(asn1, "I", &bvserno) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	} else {
		serno.val = (uchar_t *)bvserno->bv_val;
		serno.len = bvserno->bv_len;
	}

	tbscert = malloc(sizeof (KMF_X509_TBS_CERT));
	if (!tbscert) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	(void) memset(tbscert, 0, sizeof (KMF_X509_TBS_CERT));

	if ((ret = set_der_integer(&tbscert->version, version)) != KMF_OK)
		goto cleanup;

	if ((ret = set_bigint(&tbscert->serialNumber, &serno)) != KMF_OK)
		goto cleanup;

	if ((ret = get_algoid(asn1, &tbscert->signature)) != KMF_OK)
		goto cleanup;

	if ((ret = get_rdn(asn1, &tbscert->issuer)) != KMF_OK)
		goto cleanup;

	if ((ret = get_validity(asn1, &tbscert->validity)) != KMF_OK)
		goto cleanup;

	if ((ret = get_rdn(asn1, &tbscert->subject)) != KMF_OK)
		goto cleanup;

	if ((ret = get_spki(asn1, &tbscert->subjectPublicKeyInfo)) != KMF_OK)
		goto cleanup;

	/* Check for the optional fields */
	tbscert->extensions.numberOfExtensions = 0;
	tbscert->extensions.extensions = NULL;

	while ((kmfber_scanf(asn1, "t", &tag)) != -1 &&
	    (tag == 0xA1 || tag == 0xA2 || tag == 0xA3)) {
		char *optfield;
		ber_len_t len;

		/* consume the tag and length */
		(void) kmfber_scanf(asn1, "T", &tag);
		switch (tag) {
			case 0xA1:
				if (kmfber_scanf(asn1, "B", &optfield, &len) !=
				    BER_BIT_STRING) {
					ret = KMF_ERR_BAD_CERT_FORMAT;
					goto cleanup;
				}
				tbscert->issuerUniqueIdentifier.Data =
				    (uchar_t *)optfield;
				tbscert->issuerUniqueIdentifier.Length =
				    len / 8;
				break;
			case 0xA2:
				if (kmfber_scanf(asn1, "B", &optfield, &len) !=
				    BER_BIT_STRING) {
					ret = KMF_ERR_BAD_CERT_FORMAT;
					goto cleanup;
				}
				tbscert->subjectUniqueIdentifier.Data =
				    (uchar_t *)optfield;
				tbscert->subjectUniqueIdentifier.Length =
				    len / 8;
				break;
			case 0xA3:
			ret = get_extensions(asn1, &tbscert->extensions);
			break;
		}
	}

	*signed_cert_ptr_ptr = tbscert;

cleanup:
	if (bvserno != NULL) {
		free(bvserno->bv_val);
		free(bvserno);
	}
	if (ret != KMF_OK) {
		if (tbscert) {
			free_tbscert(tbscert);
			free(tbscert);
		}
		*signed_cert_ptr_ptr = NULL;
	}
	return (ret);
}

KMF_RETURN
DerDecodeTbsCertificate(const KMF_DATA *Value,
	KMF_X509_TBS_CERT **tbscert)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue 	rawcert;
	KMF_X509_TBS_CERT *newcert = NULL;

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

	rawcert.bv_val = (char *)Value->Data;
	rawcert.bv_len = Value->Length;

	if ((asn1 = kmfder_init(&rawcert)) == NULL)
		return (KMF_ERR_MEMORY);

	ret = decode_tbscert_data(asn1, &newcert);
	if (ret != KMF_OK)
		goto cleanup;

	*tbscert = newcert;

cleanup:
	if (ret != KMF_OK) {
		if (newcert)
			free_tbscert(newcert);
		*tbscert = NULL;
	}
	kmfber_free(asn1, 1);

	return (ret);
}

/*
 * Name: DerDecodeSignedCertificate
 *
 * Description:
 * DER decodes the encoded X509 certificate
 *
 * Parameters:
 * Value (input): DER encoded object that shd be decoded
 *
 * signed_cert_ptr_ptr (output) : Decoded KMF_X509_CERTIFICATE object
 */
KMF_RETURN
DerDecodeSignedCertificate(const KMF_DATA *Value,
	KMF_X509_CERTIFICATE **signed_cert_ptr_ptr)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue 	rawcert;
	ber_tag_t	tag;
	ber_len_t	size;
	char		*end = NULL;
	char		*signature;
	KMF_X509_TBS_CERT	*tbscert = NULL;
	KMF_X509_CERTIFICATE *certptr = NULL;

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

	rawcert.bv_val = (char *)Value->Data;
	rawcert.bv_len = Value->Length;

	if ((asn1 = kmfder_init(&rawcert)) == NULL)
		return (KMF_ERR_MEMORY);

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

	certptr = malloc(sizeof (KMF_X509_CERTIFICATE));
	if (certptr == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	(void) memset(certptr, 0, sizeof (KMF_X509_CERTIFICATE));

	ret = decode_tbscert_data(asn1, &tbscert);
	if (ret != KMF_OK)
		goto cleanup;

	certptr->certificate = *tbscert;
	free(tbscert);
	tbscert = NULL;

	/*
	 * The signature data my not be present yet.
	 */
	if ((ret = get_algoid(asn1,
	    &certptr->signature.algorithmIdentifier)) == KMF_OK) {

		/* Check to see if the cert has a signature yet */
		if (kmfber_next_element(asn1, &size, end) == BER_BIT_STRING) {
			/* Finally, get the encrypted signature BITSTRING */
			if (kmfber_scanf(asn1, "tl", &tag, &size) == -1) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto cleanup;
			}
			if (tag != BER_BIT_STRING) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto cleanup;
			}
			if (kmfber_scanf(asn1, "B}", &signature, &size) == -1) {
				ret = KMF_ERR_BAD_CERT_FORMAT;
				goto cleanup;
			}
			certptr->signature.encrypted.Data =
			    (uchar_t *)signature;
			certptr->signature.encrypted.Length = size / 8;
		} else {
			certptr->signature.encrypted.Data = NULL;
			certptr->signature.encrypted.Length = 0;
		}
	} else {
		(void) memset(&certptr->signature, 0,
		    sizeof (certptr->signature));
		ret = KMF_OK;
	}

	*signed_cert_ptr_ptr = certptr;
cleanup:
	if (ret != KMF_OK) {
		if (certptr) {
			free_decoded_cert(certptr);
			free(certptr);
		}

		*signed_cert_ptr_ptr = NULL;
	}
	if (asn1)
		kmfber_free(asn1, 1);

	return (ret);

}

KMF_RETURN
DerDecodeExtension(KMF_DATA *Data, KMF_X509_EXTENSION **extn)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue bv;

	bv.bv_val = (char *)Data->Data;
	bv.bv_len = Data->Length;

	asn1 = kmfder_init(&bv);
	if (asn1 == NULL)
		return (KMF_ERR_MEMORY);

	ret = get_one_extension(asn1, extn, NULL);

cleanup:
	if (ret != KMF_OK) {
		if (*extn != NULL) {
			free(*extn);
		}
		*extn = NULL;
	}

	kmfber_free(asn1, 1);
	return (ret);
}

KMF_RETURN
DerDecodeName(KMF_DATA *encodedname, KMF_X509_NAME *name)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue  bv;

	bv.bv_val = (char *)encodedname->Data;
	bv.bv_len = encodedname->Length;

	asn1 = kmfder_init(&bv);
	if (asn1 == NULL)
		return (KMF_ERR_MEMORY);

	(void) memset((void *)name, 0, sizeof (KMF_X509_NAME));

	if ((ret = get_rdn(asn1, name)) != KMF_OK)
		goto cleanup;

cleanup:
	if (asn1)
		kmfber_free(asn1, 1);
	return (ret);
}

KMF_RETURN
DerEncodeName(KMF_X509_NAME *name, KMF_DATA *encodedname)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue  *bv = NULL;

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

	if ((ret = encode_rdn(asn1, name)) != KMF_OK)
		goto cleanup;

	if (kmfber_flatten(asn1, &bv) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	encodedname->Data = (uchar_t *)bv->bv_val;
	encodedname->Length = bv->bv_len;

cleanup:
	if (bv)
		free(bv);

	if (asn1)
		kmfber_free(asn1, 1);

	return (ret);
}

static KMF_RETURN
encode_tbs_cert(BerElement *asn1, KMF_X509_TBS_CERT *tbscert)
{
	KMF_RETURN ret = KMF_OK;
	uint32_t version;

	/* version should be 4 bytes or less */
	if (tbscert->version.Length > sizeof (int))
		return (KMF_ERR_BAD_CERT_FORMAT);

	(void) memcpy(&version, tbscert->version.Data,
	    tbscert->version.Length);

	/* Start the sequence and add the version */
	if (kmfber_printf(asn1, "{Tli", 0xA0, 3, version) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}
	/* Write the serial number */
	if (kmfber_printf(asn1, "I",
	    (char *)tbscert->serialNumber.val,
	    (size_t)tbscert->serialNumber.len) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if ((ret = encode_algoid(asn1, &tbscert->signature)) != KMF_OK)
		goto cleanup;

	/* Encode the Issuer RDN */
	if ((ret = encode_rdn(asn1, &tbscert->issuer)) != KMF_OK)
		goto cleanup;

	/* Encode the Validity fields */
	if ((ret = encode_validity(asn1, &tbscert->validity)) != KMF_OK)
		goto cleanup;

	/* Encode the Subject RDN */
	if ((ret = encode_rdn(asn1, &tbscert->subject)) != KMF_OK)
		goto cleanup;

	/* Encode the Subject Public Key Info */
	if ((ret = encode_spki(asn1, &tbscert->subjectPublicKeyInfo)) != KMF_OK)
		goto cleanup;

	/* Optional field:  issuer Unique ID */
	if (tbscert->issuerUniqueIdentifier.Length > 0) {
		if ((ret = encode_uniqueid(asn1, 0xA1,
		    &tbscert->issuerUniqueIdentifier)) != KMF_OK)
			goto cleanup;
	}

	/* Optional field:  Subject Unique ID */
	if (tbscert->subjectUniqueIdentifier.Length > 0) {
		if ((ret = encode_uniqueid(asn1, 0xA2,
		    &tbscert->subjectUniqueIdentifier)) != KMF_OK)
			goto cleanup;
	}

	/* Optional field: Certificate Extensions */
	if (tbscert->extensions.numberOfExtensions > 0) {
		if ((ret = encode_extensions(asn1,
		    &tbscert->extensions)) != KMF_OK)
			goto cleanup;
	}

	/* Close out the TBSCert sequence */
	if (kmfber_printf(asn1, "}") == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

cleanup:
	/*
	 * Memory cleanup is done in the caller or in the individual
	 * encoding routines.
	 */

	return (ret);
}

KMF_RETURN
DerEncodeTbsCertificate(KMF_X509_TBS_CERT *tbs_cert_ptr,
	KMF_DATA *enc_tbs_cert_ptr)
{
	KMF_RETURN ret;
	BerElement *asn1 = NULL;
	BerValue  *tbsdata = NULL;

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

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

	ret = encode_tbs_cert(asn1, tbs_cert_ptr);
	if (ret != KMF_OK)
		goto cleanup;

	if (kmfber_flatten(asn1, &tbsdata) == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	enc_tbs_cert_ptr->Data = (uchar_t *)tbsdata->bv_val;
	enc_tbs_cert_ptr->Length = tbsdata->bv_len;

cleanup:
	if (ret != KMF_OK)
		free_data(enc_tbs_cert_ptr);

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

	if (tbsdata)
		free(tbsdata);

	return (ret);
}

KMF_RETURN
DerEncodeSignedCertificate(KMF_X509_CERTIFICATE *signed_cert_ptr,
	KMF_DATA *encodedcert)
{
	KMF_RETURN ret = KMF_OK;
	KMF_X509_TBS_CERT *tbscert = NULL;
	KMF_X509_SIGNATURE		*signature = NULL;
	BerElement	*asn1 = NULL;
	BerValue 	*tbsdata = NULL;

	if (signed_cert_ptr == NULL || encodedcert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

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

	tbscert = &signed_cert_ptr->certificate;
	signature = &signed_cert_ptr->signature;

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

	/* Start outer X509 Certificate SEQUENCE */
	if (kmfber_printf(asn1, "{") == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if ((ret = encode_tbs_cert(asn1, tbscert)) != KMF_OK) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	/* Add the Algorithm & Signature Sequence */
	if ((ret = encode_algoid(asn1,
	    &signature->algorithmIdentifier)) != KMF_OK)
		goto cleanup;

	if (signature->encrypted.Length > 0) {
		if (kmfber_printf(asn1, "B", signature->encrypted.Data,
		    signature->encrypted.Length * 8) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
	}

	if (kmfber_printf(asn1, "}") == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if (kmfber_flatten(asn1, &tbsdata) == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	encodedcert->Data = (uchar_t *)tbsdata->bv_val;
	encodedcert->Length = tbsdata->bv_len;

cleanup:
	if (ret != KMF_OK)
		free_data(encodedcert);

	if (tbsdata)
		free(tbsdata);

	if (asn1)
		kmfber_free(asn1, 1);

	return (ret);
}

KMF_RETURN
ExtractX509CertParts(KMF_DATA *x509cert, KMF_DATA *tbscert,
		KMF_DATA *signature)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *der = NULL;
	BerValue x509;
	ber_tag_t tag;
	ber_len_t size;

	if (tbscert == NULL || x509cert == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	x509.bv_val = (char *)x509cert->Data;
	x509.bv_len = x509cert->Length;

	der = kmfder_init(&x509);
	if (der == NULL)
		return (KMF_ERR_MEMORY);

	/* Skip over the overall Sequence tag to get at the TBS Cert data */
	if (kmfber_scanf(der, "Tl", &tag, &size) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}
	if (tag != BER_CONSTRUCTED_SEQUENCE) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	/*
	 * Since we are extracting a copy of the ENCODED bytes, we
	 * must make sure to also include the bytes for the tag and
	 * the length fields for the CONSTRUCTED SEQUENCE (TBSCert).
	 */
	size += kmfber_calc_taglen(tag) + kmfber_calc_lenlen(size);

	tbscert->Data = malloc(size);
	if (tbscert->Data == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	tbscert->Length = size;

	/* The der data ptr is now set to the start of the TBS cert sequence */
	size = kmfber_read(der, (char *)tbscert->Data, tbscert->Length);
	if (size != tbscert->Length) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if (signature != NULL) {
		KMF_X509_ALGORITHM_IDENTIFIER algoid;
		if ((ret = get_algoid(der, &algoid)) != KMF_OK)
			goto cleanup;
		free_algoid(&algoid);

		if (kmfber_scanf(der, "tl", &tag, &size) != BER_BIT_STRING) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
		/* Now get the signature data */
		if (kmfber_scanf(der, "B", (char **)&signature->Data,
		    (ber_len_t *)&signature->Length) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
		/* convert bitstring length to bytes */
		signature->Length = signature->Length / 8;
	}

cleanup:
	if (der)
		kmfber_free(der, 1);

	if (ret != KMF_OK)
		free_data(tbscert);

	return (ret);
}

/*
 * Name: GetKeyFromSpki
 *
 * Description:
 * This function parses the KMF_X509_SPKI into its
 * key and parameter components based on the key generation algorithm.
 * NOTE:  Currently, it only checks for the RSA and DSA algorithms.
 *	The RSA algorithm is equivalent to the default behavior.
 *	All other algorithms will default to the parameters = NULL and the
 *	key data equal to whatever is in the CSSM_KEY structure for the key
 *
 * Parameters:
 * AlgId (input) : Algorithm identifier
 * SpkiPtr (input): SPKI structure that contains the key
 * key_ptr(output): The output key
 *
 */
KMF_RETURN
GetKeyFromSpki(KMF_ALGORITHM_INDEX AlgId,
	KMF_X509_SPKI *SpkiPtr,
	KMF_DATA **key_ptr)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1;
	BerValue *encodedkey = NULL;

	if (!key_ptr || !SpkiPtr) {
		return (KMF_ERR_BAD_PARAMETER);
	}
	*key_ptr = NULL;

	switch (AlgId) {
		case KMF_ALGID_DSA:
			asn1 = kmfder_alloc();
			if (asn1 == NULL) {
				return (KMF_ERR_MEMORY);
			}

			if ((ret = encode_spki(asn1, SpkiPtr)) != KMF_OK) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}

			if (kmfber_flatten(asn1, &encodedkey) == -1) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}

			*key_ptr = malloc(sizeof (KMF_DATA));

			if (!*key_ptr) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}

			(*key_ptr)->Length = encodedkey->bv_len;
			(*key_ptr)->Data = (uchar_t *)encodedkey->bv_val;
cleanup:
			kmfber_free(asn1, 1);
			if (encodedkey)
				free(encodedkey);
		break;
		default: /* RSA */
			*key_ptr = malloc(sizeof (KMF_DATA));

			if (!*key_ptr) {
				return (KMF_ERR_MEMORY);
			}
			(*key_ptr)->Length = SpkiPtr->subjectPublicKey.Length;
			(*key_ptr)->Data = malloc((*key_ptr)->Length);

			if (!(*key_ptr)->Data) {
				free(*key_ptr);
				*key_ptr = NULL;
				return (KMF_ERR_MEMORY);
			}
			(void) memcpy((*key_ptr)->Data,
			    SpkiPtr->subjectPublicKey.Data,
			    (*key_ptr)->Length);
			return (ret);
	}
	return (ret);
}

static KMF_RETURN
decode_csr_extensions(BerElement *asn1, KMF_X509_EXTENSIONS *extns)
{
	KMF_RETURN ret = KMF_OK;
	BerValue oid;

	if (kmfber_scanf(asn1, "{D", &oid) == -1) {
		return (KMF_ERR_UNKNOWN_CSR_ATTRIBUTE);
	}

	/* We only understand extension requests in a CSR */
	if (memcmp(oid.bv_val, extension_request_oid.Data,
	    oid.bv_len) != 0) {
		return (KMF_ERR_UNKNOWN_CSR_ATTRIBUTE);
	}

	if (kmfber_scanf(asn1, "[") == -1) {
		return (KMF_ERR_ENCODING);
	}
	ret = get_extensions(asn1, extns);


	return (ret);
}

static KMF_RETURN
decode_tbscsr_data(BerElement *asn1,
	KMF_TBS_CSR **signed_csr_ptr_ptr)
{
	KMF_RETURN ret = KMF_OK;
	KMF_TBS_CSR	*tbscsr = NULL;
	char *end = NULL;
	uint32_t version;
	ber_tag_t tag;
	ber_len_t size;

	/* Now get the version number, it is not optional */
	if (kmfber_scanf(asn1, "{i", &version) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	tbscsr = malloc(sizeof (KMF_TBS_CSR));
	if (!tbscsr) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	(void) memset(tbscsr, 0, sizeof (KMF_TBS_CSR));

	if ((ret = set_der_integer(&tbscsr->version, version)) != KMF_OK)
		goto cleanup;

	if ((ret = get_rdn(asn1, &tbscsr->subject)) != KMF_OK)
		goto cleanup;

	if ((ret = get_spki(asn1, &tbscsr->subjectPublicKeyInfo)) != KMF_OK)
		goto cleanup;

	/* Check for the optional fields (attributes) */
	if (kmfber_next_element(asn1, &size, end) == 0xA0) {
		if (kmfber_scanf(asn1, "Tl", &tag, &size) == -1) {
			ret = KMF_ERR_ENCODING;
			goto cleanup;
		}

		ret = decode_csr_extensions(asn1, &tbscsr->extensions);
	}
	if (ret == KMF_OK)
		*signed_csr_ptr_ptr = tbscsr;

cleanup:
	if (ret != KMF_OK) {
		if (tbscsr) {
			free_tbscsr(tbscsr);
			free(tbscsr);
		}
		*signed_csr_ptr_ptr = NULL;
	}
	return (ret);
}

KMF_RETURN
DerDecodeTbsCsr(const KMF_DATA *Value,
	KMF_TBS_CSR **tbscsr)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue 	rawcsr;
	KMF_TBS_CSR *newcsr = NULL;

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

	rawcsr.bv_val = (char *)Value->Data;
	rawcsr.bv_len = Value->Length;

	if ((asn1 = kmfder_init(&rawcsr)) == NULL)
		return (KMF_ERR_MEMORY);

	ret = decode_tbscsr_data(asn1, &newcsr);
	if (ret != KMF_OK)
		goto cleanup;

	*tbscsr = newcsr;

cleanup:
	if (ret != KMF_OK) {
		if (newcsr)
			free_tbscsr(newcsr);
		*tbscsr = NULL;
	}
	kmfber_free(asn1, 1);

	return (ret);
}

KMF_RETURN
DerDecodeSignedCsr(const KMF_DATA *Value,
	KMF_CSR_DATA **signed_csr_ptr_ptr)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue 	rawcsr;
	int			tag;
	ber_len_t	size;
	char		*end = NULL;
	char		*signature;
	KMF_TBS_CSR	*tbscsr = NULL;
	KMF_CSR_DATA *csrptr = NULL;

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

	rawcsr.bv_val = (char *)Value->Data;
	rawcsr.bv_len = Value->Length;

	if ((asn1 = kmfder_init(&rawcsr)) == NULL)
		return (KMF_ERR_MEMORY);

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

	csrptr = malloc(sizeof (KMF_CSR_DATA));
	if (csrptr == NULL) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}
	(void) memset(csrptr, 0, sizeof (KMF_CSR_DATA));

	ret = decode_tbscsr_data(asn1, &tbscsr);
	if (ret != KMF_OK)
		goto cleanup;

	csrptr->csr = *tbscsr;
	free(tbscsr);
	tbscsr = NULL;

	if ((ret = get_algoid(asn1,
	    &csrptr->signature.algorithmIdentifier)) != KMF_OK)
		goto cleanup;

	/* Check to see if the cert has a signature yet */
	if (kmfber_next_element(asn1, &size, end) == BER_BIT_STRING) {
		/* Finally, get the encrypted signature BITSTRING */
		if (kmfber_scanf(asn1, "tl", &tag, &size) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
		if (tag != BER_BIT_STRING) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
		if (kmfber_scanf(asn1, "B}", &signature, &size) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
		csrptr->signature.encrypted.Data = (uchar_t *)signature;
		csrptr->signature.encrypted.Length = size / 8;
	} else {
		csrptr->signature.encrypted.Data = NULL;
		csrptr->signature.encrypted.Length = 0;
	}

	*signed_csr_ptr_ptr = csrptr;
cleanup:
	if (ret != KMF_OK) {
		free_tbscsr(&csrptr->csr);
		free_algoid(&csrptr->signature.algorithmIdentifier);
		if (csrptr->signature.encrypted.Data)
			free(csrptr->signature.encrypted.Data);

		if (csrptr)
			free(csrptr);

		*signed_csr_ptr_ptr = NULL;
	}
	if (asn1)
		kmfber_free(asn1, 1);

	return (ret);

}

static KMF_RETURN
encode_csr_extensions(BerElement *asn1, KMF_TBS_CSR *tbscsr)
{
	KMF_RETURN ret = KMF_OK;
	int attlen = 0;
	BerElement *extnasn1 = NULL;
	BerValue *extnvalue = NULL;

	/* Optional field: CSR attributes and extensions */
	if (tbscsr->extensions.numberOfExtensions > 0) {
		if (kmfber_printf(asn1, "T", 0xA0) == -1) {
			ret = KMF_ERR_ENCODING;
			goto cleanup;
		}
	} else {
		/* No extensions or attributes to encode */
		return (KMF_OK);
	}

	/*
	 * attributes [0] Attributes
	 * Attributes := SET OF Attribute
	 * Attribute  := SEQUENCE {
	 *   { ATTRIBUTE ID
	 *	values SET SIZE(1..MAX) of ATTRIBUTE
	 *   }
	 *
	 * Ex: { ExtensionRequest OID [ { {extn1 } , {extn2 } } ] }
	 */

	/*
	 * Encode any extensions and add to the attributes section.
	 */
	if (tbscsr->extensions.numberOfExtensions > 0) {
		extnasn1 = kmfder_alloc();
		if (extnasn1 == NULL) {
			ret = KMF_ERR_MEMORY;
			goto cleanup;
		}

		if (kmfber_printf(extnasn1, "{D[{",
		    &extension_request_oid) == -1) {
			ret = KMF_ERR_ENCODING;
			goto cleanup_1;
		}

		if ((ret = encode_extension_list(extnasn1,
		    &tbscsr->extensions)) != KMF_OK) {
			goto cleanup_1;
		}

		if (kmfber_printf(extnasn1, "}]}") == -1) {
			ret = KMF_ERR_ENCODING;
			goto cleanup_1;
		}

		if (kmfber_flatten(extnasn1, &extnvalue) == -1) {
			ret = KMF_ERR_MEMORY;
			goto cleanup_1;
		}
cleanup_1:
		kmfber_free(extnasn1, 1);

		if (ret == KMF_OK)
			/* Add 2 bytes to cover the tag and the length */
			attlen = extnvalue->bv_len;
	}
	if (ret != KMF_OK)
		goto cleanup;

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

	/* Write the actual encoded extensions */
	if (extnvalue != NULL && extnvalue->bv_val != NULL) {
		if (kmfber_write(asn1, extnvalue->bv_val,
		    extnvalue->bv_len, 0) == -1) {
			ret = KMF_ERR_ENCODING;
			goto cleanup;
		}
	}

cleanup:
	/*
	 * Memory cleanup is done in the caller or in the individual
	 * encoding routines.
	 */
	if (extnvalue) {
		if (extnvalue->bv_val)
			free(extnvalue->bv_val);
		free(extnvalue);
	}

	return (ret);
}

static KMF_RETURN
encode_tbs_csr(BerElement *asn1, KMF_TBS_CSR *tbscsr)
{
	KMF_RETURN ret = KMF_OK;
	uint32_t version;

	/* Start the version */
	(void) memcpy(&version, tbscsr->version.Data,
	    tbscsr->version.Length);

	if (kmfber_printf(asn1, "{i", version) == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	/* Encode the Subject RDN */
	if ((ret = encode_rdn(asn1, &tbscsr->subject)) != KMF_OK)
		goto cleanup;

	/* Encode the Subject Public Key Info */
	if ((ret = encode_spki(asn1, &tbscsr->subjectPublicKeyInfo)) != KMF_OK)
		goto cleanup;


	if ((ret = encode_csr_extensions(asn1, tbscsr)) != KMF_OK)
		goto cleanup;

	/* Close out the TBSCert sequence */
	if (kmfber_printf(asn1, "}") == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

cleanup:
	return (ret);
}

KMF_RETURN
DerEncodeDSAPrivateKey(KMF_DATA *encodedkey, KMF_RAW_DSA_KEY *dsa)
{
	KMF_RETURN rv = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue  *dsadata = NULL;

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

	if (kmfber_printf(asn1, "I",
	    dsa->value.val, dsa->value.len) == -1) {
		rv = KMF_ERR_MEMORY;
		goto cleanup;
	}

	if (kmfber_flatten(asn1, &dsadata) == -1) {
		rv = KMF_ERR_MEMORY;
		goto cleanup;
	}

	encodedkey->Data = (uchar_t *)dsadata->bv_val;
	encodedkey->Length = dsadata->bv_len;

	free(dsadata);
cleanup:
	kmfber_free(asn1, 1);
	return (rv);
}

KMF_RETURN
DerEncodeRSAPrivateKey(KMF_DATA *encodedkey, KMF_RAW_RSA_KEY *rsa)
{
	KMF_RETURN rv = KMF_OK;
	BerElement *asn1 = NULL;
	uchar_t ver = 0;
	BerValue  *rsadata = NULL;

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

	if (kmfber_printf(asn1, "{IIIIIIIII}",
	    &ver, 1,
	    rsa->mod.val, rsa->mod.len,
	    rsa->pubexp.val, rsa->pubexp.len,
	    rsa->priexp.val, rsa->priexp.len,
	    rsa->prime1.val, rsa->prime1.len,
	    rsa->prime2.val, rsa->prime2.len,
	    rsa->exp1.val, rsa->exp1.len,
	    rsa->exp2.val, rsa->exp2.len,
	    rsa->coef.val, rsa->coef.len) == -1)
		goto cleanup;

	if (kmfber_flatten(asn1, &rsadata) == -1) {
		rv = KMF_ERR_MEMORY;
		goto cleanup;
	}

	encodedkey->Data = (uchar_t *)rsadata->bv_val;
	encodedkey->Length = rsadata->bv_len;

	free(rsadata);
cleanup:
	kmfber_free(asn1, 1);
	return (rv);
}


KMF_RETURN
DerEncodeTbsCsr(KMF_TBS_CSR *tbs_csr_ptr,
	KMF_DATA *enc_tbs_csr_ptr)
{
	KMF_RETURN ret;
	BerValue  *tbsdata = NULL;
	BerElement *asn1 = NULL;

	asn1 = kmfder_alloc();

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

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

	ret = encode_tbs_csr(asn1, tbs_csr_ptr);
	if (ret != KMF_OK)
		goto cleanup;

	if (kmfber_flatten(asn1, &tbsdata) == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	enc_tbs_csr_ptr->Data = (uchar_t *)tbsdata->bv_val;
	enc_tbs_csr_ptr->Length = tbsdata->bv_len;

cleanup:
	if (ret != KMF_OK)
		free_data(enc_tbs_csr_ptr);

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

	if (tbsdata)
		free(tbsdata);

	return (ret);
}

KMF_RETURN
DerEncodeSignedCsr(KMF_CSR_DATA *signed_csr_ptr,
	KMF_DATA *encodedcsr)
{
	KMF_RETURN ret = KMF_OK;
	KMF_TBS_CSR *tbscsr = NULL;
	KMF_X509_SIGNATURE		*signature = NULL;
	BerElement	*asn1 = NULL;
	BerValue 	*tbsdata = NULL;

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

	tbscsr = &signed_csr_ptr->csr;
	signature = &signed_csr_ptr->signature;

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

	/* Start outer CSR SEQUENCE */
	if (kmfber_printf(asn1, "{") == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	ret = encode_tbs_csr(asn1, tbscsr);

	/* Add the Algorithm & Signature Sequence */
	if ((ret = encode_algoid(asn1,
	    &signature->algorithmIdentifier)) != KMF_OK)
		goto cleanup;

	if (signature->encrypted.Length > 0) {
		if (kmfber_printf(asn1, "B", signature->encrypted.Data,
		    signature->encrypted.Length * 8) == -1) {
			ret = KMF_ERR_BAD_CERT_FORMAT;
			goto cleanup;
		}
	}

	if (kmfber_printf(asn1, "}") == -1) {
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	if (kmfber_flatten(asn1, &tbsdata) == -1) {
		ret = KMF_ERR_MEMORY;
		goto cleanup;
	}

	encodedcsr->Data = (uchar_t *)tbsdata->bv_val;
	encodedcsr->Length = tbsdata->bv_len;

cleanup:
	if (ret != KMF_OK) {
		free_data(encodedcsr);
	}

	if (tbsdata)
		free(tbsdata);

	if (asn1)
		kmfber_free(asn1, 1);
	return (ret);
}

KMF_RETURN
ExtractSPKIData(
	const KMF_X509_SPKI *pKey,
	KMF_ALGORITHM_INDEX AlgorithmId,
	KMF_DATA *pKeyParts,
	uint32_t *uNumKeyParts)
{
	KMF_RETURN ret = KMF_OK;
	BerElement *asn1 = NULL;
	BerValue 	*P, *Q, *G, *Mod, *Exp, *PubKey;
	BerValue	PubKeyParams, PubKeyData;

	if (pKeyParts == NULL || uNumKeyParts == NULL || pKey == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	switch (AlgorithmId) {
		case KMF_ALGID_DSA:
		case KMF_ALGID_SHA1WithDSA:
			*uNumKeyParts = 0;
			/* Get the parameters from the algorithm definition */
			PubKeyParams.bv_val =
			    (char *)pKey->algorithm.parameters.Data;
			PubKeyParams.bv_len = pKey->algorithm.parameters.Length;
			if ((asn1 = kmfder_init(&PubKeyParams)) == NULL)
				return (KMF_ERR_MEMORY);

			if (kmfber_scanf(asn1, "{III}", &P, &Q, &G) == -1) {
				kmfber_free(asn1, 1);
				return (KMF_ERR_BAD_KEY_FORMAT);
			}
			pKeyParts[KMF_DSA_PRIME].Data = (uchar_t *)P->bv_val;
			pKeyParts[KMF_DSA_PRIME].Length = P->bv_len;
			pKeyParts[KMF_DSA_SUB_PRIME].Data =
			    (uchar_t *)Q->bv_val;
			pKeyParts[KMF_DSA_SUB_PRIME].Length = Q->bv_len;
			pKeyParts[KMF_DSA_BASE].Data = (uchar_t *)G->bv_val;
			pKeyParts[KMF_DSA_BASE].Length = G->bv_len;

			free(P);
			free(Q);
			free(G);
			kmfber_free(asn1, 1);

			/* Get the PubKey data */
			PubKeyData.bv_val = (char *)pKey->subjectPublicKey.Data;
			PubKeyData.bv_len = pKey->subjectPublicKey.Length;
			if ((asn1 = kmfder_init(&PubKeyData)) == NULL) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}
			PubKey = NULL;
			if (kmfber_scanf(asn1, "I", &PubKey) == -1) {
				ret = KMF_ERR_BAD_KEY_FORMAT;
				goto cleanup;
			}
			pKeyParts[KMF_DSA_PUBLIC_VALUE].Data =
			    (uchar_t *)PubKey->bv_val;
			pKeyParts[KMF_DSA_PUBLIC_VALUE].Length = PubKey->bv_len;

			free(PubKey);

			*uNumKeyParts = KMF_NUMBER_DSA_PUBLIC_KEY_PARTS;
			break;

		case KMF_ALGID_RSA:
		case KMF_ALGID_MD2WithRSA:
		case KMF_ALGID_MD5WithRSA:
		case KMF_ALGID_SHA1WithRSA:
			*uNumKeyParts = 0;
			PubKeyData.bv_val = (char *)pKey->subjectPublicKey.Data;
			PubKeyData.bv_len = pKey->subjectPublicKey.Length;
			if ((asn1 = kmfder_init(&PubKeyData)) == NULL) {
				ret = KMF_ERR_MEMORY;
				goto cleanup;
			}
			if (kmfber_scanf(asn1, "{II}", &Mod, &Exp) == -1) {
				ret = KMF_ERR_BAD_KEY_FORMAT;
				goto cleanup;
			}
			pKeyParts[KMF_RSA_MODULUS].Data =
			    (uchar_t *)Mod->bv_val;
			pKeyParts[KMF_RSA_MODULUS].Length = Mod->bv_len;
			pKeyParts[KMF_RSA_PUBLIC_EXPONENT].Data =
			    (uchar_t *)Exp->bv_val;
			pKeyParts[KMF_RSA_PUBLIC_EXPONENT].Length = Exp->bv_len;
			*uNumKeyParts = KMF_NUMBER_RSA_PUBLIC_KEY_PARTS;

			free(Mod);
			free(Exp);
			break;
		default:
			return (KMF_ERR_BAD_PARAMETER);
	}
cleanup:
	if (ret != KMF_OK) {
		int i;
		for (i = 0; i < *uNumKeyParts; i++)
			free_data(&pKeyParts[i]);
	}
	if (asn1 != NULL) {
		kmfber_free(asn1, 1);
	}

	return (ret);
}