/*
 * 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * This file implements the sign CSR operation for this tool.
 */

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <cryptoutil.h>
#include <security/cryptoki.h>
#include "common.h"

#include <kmfapi.h>
#include <kmfapiP.h>

#define	SET_VALUE(f, s) \
	rv = f; \
	if (rv != KMF_OK) { \
		cryptoerror(LOG_STDERR, \
		    gettext("Failed to set %s: 0x%02x\n"), s, rv); \
		goto cleanup; \
	}


static int
read_csrdata(KMF_HANDLE_T handle, char *csrfile, KMF_CSR_DATA *csrdata)
{
	KMF_RETURN rv = KMF_OK;
	KMF_ENCODE_FORMAT csrfmt;
	KMF_DATA csrfiledata = { 0, NULL };
	KMF_DATA rawcsr = { 0, NULL };

	rv = kmf_get_file_format(csrfile, &csrfmt);
	if (rv != KMF_OK)
		return (rv);

	rv = kmf_read_input_file(handle, csrfile, &csrfiledata);
	if (rv != KMF_OK)
		return (rv);

	if (csrfmt == KMF_FORMAT_PEM) {
		rv = kmf_pem_to_der(csrfiledata.Data, csrfiledata.Length,
		    &rawcsr.Data, (int *)&rawcsr.Length);
		if (rv != KMF_OK)
			return (rv);

		kmf_free_data(&csrfiledata);
	} else {
		rawcsr.Data = csrfiledata.Data;
		rawcsr.Length = csrfiledata.Length;
	}

	rv = kmf_decode_csr(handle, &rawcsr, csrdata);
	kmf_free_data(&rawcsr);

	return (rv);
}

static KMF_RETURN
find_csr_extn(KMF_X509_EXTENSIONS *extnlist, KMF_OID *extoid,
	KMF_X509_EXTENSION *outextn)
{
	int i, found = 0;
	KMF_X509_EXTENSION *eptr;
	KMF_RETURN rv = KMF_OK;

	(void) memset(outextn, 0, sizeof (KMF_X509_EXTENSION));
	for (i = 0; !found && i < extnlist->numberOfExtensions; i++) {
		eptr = &extnlist->extensions[i];
		if (IsEqualOid(extoid, &eptr->extnId)) {
			rv = copy_extension_data(outextn, eptr);
			found++;
		}
	}
	if (found == 0 || rv != KMF_OK)
		return (1);
	else
		return (rv);
}

static int
build_cert_from_csr(KMF_CSR_DATA *csrdata,
	KMF_X509_CERTIFICATE *signedCert,
	KMF_BIGINT *serial,
	uint32_t ltime,
	char *issuer, char *subject,
	char *altname,
	KMF_GENERALNAMECHOICES alttype,
	int altcrit,
	uint16_t kubits,
	int kucrit,
	EKU_LIST *ekulist)
{
	KMF_RETURN rv = KMF_OK;
	KMF_X509_NAME issuerDN, subjectDN;

	/*
	 * If the CSR is ok, now we can generate the final certificate.
	 */
	(void) memset(signedCert, 0, sizeof (KMF_X509_CERTIFICATE));
	(void) memset(&issuerDN, 0, sizeof (issuerDN));
	(void) memset(&subjectDN, 0, sizeof (subjectDN));

	SET_VALUE(kmf_set_cert_version(signedCert, 2), "version number");

	SET_VALUE(kmf_set_cert_serial(signedCert, serial), "serial number");

	SET_VALUE(kmf_set_cert_validity(signedCert, NULL, ltime),
	    "validity time");

	if (issuer) {
		if (kmf_dn_parser(issuer, &issuerDN) != KMF_OK) {
			cryptoerror(LOG_STDERR,
			    gettext("Issuer name cannot be parsed\n"));
			return (PK_ERR_USAGE);
		}
		SET_VALUE(kmf_set_cert_issuer(signedCert, &issuerDN),
		    "Issuer Name");
	}
	if (subject) {
		if (kmf_dn_parser(subject, &subjectDN) != KMF_OK) {
			cryptoerror(LOG_STDERR,
			    gettext("Subject name cannot be parsed\n"));
			return (PK_ERR_USAGE);
		}
		SET_VALUE(kmf_set_cert_subject(signedCert, &subjectDN),
		    "Subject Name");
	} else {
		signedCert->certificate.subject = csrdata->csr.subject;
	}

	signedCert->certificate.subjectPublicKeyInfo =
	    csrdata->csr.subjectPublicKeyInfo;

	signedCert->certificate.extensions = csrdata->csr.extensions;

	signedCert->certificate.signature =
	    csrdata->signature.algorithmIdentifier;

	if (kubits != 0) {
		KMF_X509_EXTENSION extn;
		uint16_t oldbits;
		/*
		 * If the CSR already has KU, merge them.
		 */
		rv = find_csr_extn(&csrdata->csr.extensions,
		    (KMF_OID *)&KMFOID_KeyUsage, &extn);
		if (rv == KMF_OK) {
			extn.critical |= kucrit;
			if (extn.value.tagAndValue->value.Length > 1) {
				oldbits =
				    extn.value.tagAndValue->value.Data[1] << 8;
			} else {
				oldbits =
				    extn.value.tagAndValue->value.Data[0];
			}
			oldbits |= kubits;
		} else {
			SET_VALUE(kmf_set_cert_ku(signedCert, kucrit, kubits),
			    "KeyUsage");
		}
	}
	if (altname != NULL) {
		SET_VALUE(kmf_set_cert_subject_altname(signedCert,
		    altcrit, alttype, altname), "subjectAltName");
	}
	if (ekulist != NULL) {
		int i;
		for (i = 0; rv == KMF_OK && i < ekulist->eku_count; i++) {
			SET_VALUE(kmf_add_cert_eku(signedCert,
			    &ekulist->ekulist[i],
			    ekulist->critlist[i]), "Extended Key Usage");
		}
	}
cleanup:
	if (issuer != NULL)
		kmf_free_dn(&issuerDN);
	if (subject != NULL)
		kmf_free_dn(&subjectDN);

	return (rv);
}

static int
pk_sign_cert(KMF_HANDLE_T handle, KMF_X509_CERTIFICATE *cert,
	KMF_KEY_HANDLE *key, KMF_OID *sigoid, KMF_DATA *outdata)
{
	KMF_RETURN rv;
	int numattr;
	KMF_ATTRIBUTE attrlist[4];

	numattr = 0;
	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR,
	    &key->kstype, sizeof (KMF_KEYSTORE_TYPE));

	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_HANDLE_ATTR,
	    key, sizeof (KMF_KEY_HANDLE_ATTR));

	/* cert data that is to be signed */
	kmf_set_attr_at_index(attrlist, numattr++, KMF_X509_CERTIFICATE_ATTR,
	    cert, sizeof (KMF_X509_CERTIFICATE));

	/* output buffer for the signed cert */
	kmf_set_attr_at_index(attrlist, numattr++, KMF_CERT_DATA_ATTR,
	    outdata, sizeof (KMF_DATA));

	/* Set the signature OID value so KMF knows how to generate the sig */
	if (sigoid) {
		kmf_set_attr_at_index(attrlist, numattr++, KMF_OID_ATTR,
		    sigoid, sizeof (KMF_OID));
	}

	if ((rv = kmf_sign_cert(handle, numattr, attrlist)) != KMF_OK) {
		cryptoerror(LOG_STDERR,
		    gettext("Failed to sign certificate.\n"));
		return (rv);
	}

	return (rv);
}

static int
pk_signcsr_files(KMF_HANDLE_T handle,
	char *signkey,
	char *csrfile,
	KMF_BIGINT *serial,
	char *certfile,
	char *issuer,
	char *subject,
	char *altname,
	KMF_GENERALNAMECHOICES alttype,
	int altcrit,
	uint16_t kubits,
	int kucrit,
	EKU_LIST *ekulist,
	uint32_t ltime,
	KMF_ENCODE_FORMAT fmt)
{
	KMF_RETURN rv = KMF_OK;
	KMF_CSR_DATA csrdata;
	KMF_ATTRIBUTE attrlist[16];
	KMF_X509_CERTIFICATE signedCert;
	KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_OPENSSL;
	KMF_KEY_CLASS keyclass = KMF_ASYM_PRI;
	KMF_KEY_HANDLE cakey;
	KMF_DATA certdata = { 0, NULL };
	int numattr, count;

	(void) memset(&cakey, 0, sizeof (cakey));
	(void) memset(&signedCert, 0, sizeof (signedCert));

	rv = read_csrdata(handle, csrfile, &csrdata);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR,
		    gettext("Error reading CSR data\n"));
		return (rv);
	}

	/* verify the signature first */
	numattr = 0;
	kmf_set_attr_at_index(attrlist, numattr, KMF_CSR_DATA_ATTR,
	    &csrdata, sizeof (csrdata));
	numattr++;

	rv = kmf_verify_csr(handle, numattr, attrlist);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR, gettext("CSR signature "
		    "verification failed.\n"));
		goto cleanup;
	}

	rv = build_cert_from_csr(&csrdata, &signedCert, serial, ltime,
	    issuer, subject, altname, alttype, altcrit, kubits,
	    kucrit, ekulist);

	if (rv != KMF_OK)
		goto cleanup;

	/*
	 * Find the signing key.
	 */
	(void) memset(&cakey, 0, sizeof (cakey));

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

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEY_FILENAME_ATTR,
	    signkey, strlen(signkey));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEYCLASS_ATTR,
	    &keyclass, sizeof (keyclass));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEY_HANDLE_ATTR,
	    &cakey, sizeof (cakey));
	numattr++;

	count = 1;
	kmf_set_attr_at_index(attrlist, numattr, KMF_COUNT_ATTR,
	    &count, sizeof (count));
	numattr++;

	rv = kmf_find_key(handle, numattr, attrlist);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR, gettext(
		    "Error finding CA signing key\n"));
		goto cleanup;
	}

	rv = pk_sign_cert(handle, &signedCert, &cakey, NULL, &certdata);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR, gettext(
		    "Error signing certificate.\n"));
		goto cleanup;
	}

	rv = kmf_create_cert_file(&certdata, fmt, certfile);

cleanup:
	kmf_free_signed_csr(&csrdata);
	kmf_free_data(&certdata);
	kmf_free_kmf_key(handle, &cakey);
	return (rv);
}

static int
pk_signcsr_pk11_nss(KMF_HANDLE_T handle,
	KMF_KEYSTORE_TYPE kstype,
	char *dir, char *prefix,
	char *token, KMF_CREDENTIAL *cred,
	char *signkey, char *csrfile,
	KMF_BIGINT *serial, char *certfile, char *issuer, char *subject,
	char *altname, KMF_GENERALNAMECHOICES alttype, int altcrit,
	uint16_t kubits, int kucrit,
	EKU_LIST *ekulist, uint32_t ltime,
	KMF_ENCODE_FORMAT fmt, int store, char *outlabel)
{
	KMF_RETURN rv = KMF_OK;
	KMF_DATA outcert = { 0, NULL };
	KMF_CSR_DATA csrdata = { 0, NULL };
	KMF_KEY_HANDLE casignkey;
	KMF_KEY_CLASS keyclass = KMF_ASYM_PRI;
	KMF_ATTRIBUTE attrlist[16];
	KMF_X509_CERTIFICATE signedCert;
	boolean_t token_bool = B_TRUE;
	boolean_t private_bool = B_TRUE;
	int numattr = 0;
	int keys = 1;

	(void) memset(&casignkey, 0, sizeof (KMF_KEY_HANDLE));
	(void) memset(&signedCert, 0, sizeof (signedCert));

	rv = read_csrdata(handle, csrfile, &csrdata);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR,
		    gettext("Error reading CSR data\n"));
		return (rv);
	}

	if (kstype == KMF_KEYSTORE_PK11TOKEN) {
		rv = select_token(handle, token, FALSE);
	} else if (kstype == KMF_KEYSTORE_NSS) {
		rv = configure_nss(handle, dir, prefix);
	}

	/* verify the signature first */
	kmf_set_attr_at_index(attrlist, numattr, KMF_CSR_DATA_ATTR,
	    &csrdata, sizeof (csrdata));
	numattr++;

	rv = kmf_verify_csr(handle, numattr, attrlist);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR, gettext("CSR signature "
		    "verification failed.\n"));
		goto cleanup;
	}

	rv = build_cert_from_csr(&csrdata,
	    &signedCert, serial, ltime,
	    issuer, subject, altname,
	    alttype, altcrit, kubits,
	    kucrit, ekulist);

	if (rv != KMF_OK)
		goto cleanup;

	/*
	 * Find the signing key.
	 */
	numattr = 0;
	kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR,
	    &kstype, sizeof (kstype));
	numattr++;
	if (kstype == KMF_KEYSTORE_NSS) {
		kmf_set_attr_at_index(attrlist, numattr, KMF_TOKEN_LABEL_ATTR,
		    token, strlen(token));
		numattr++;
	}

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEYLABEL_ATTR, signkey,
	    strlen(signkey));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_PRIVATE_BOOL_ATTR,
	    &private_bool, sizeof (private_bool));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_TOKEN_BOOL_ATTR,
	    &token_bool, sizeof (token_bool));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEYCLASS_ATTR,
	    &keyclass, sizeof (keyclass));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_CREDENTIAL_ATTR,
	    cred, sizeof (KMF_CREDENTIAL_ATTR));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_COUNT_ATTR,
	    &keys, sizeof (keys));
	numattr++;

	kmf_set_attr_at_index(attrlist, numattr, KMF_KEY_HANDLE_ATTR,
	    &casignkey, sizeof (casignkey));
	numattr++;

	rv = kmf_find_key(handle, numattr, attrlist);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR,
		    gettext("Failed to find signing key\n"));
		goto cleanup;
	}
	/*
	 * If we found the key, now we can sign the cert.
	 */
	rv = pk_sign_cert(handle, &signedCert, &casignkey, NULL,
	    &outcert);
	if (rv != KMF_OK) {
		cryptoerror(LOG_STDERR, gettext(
		    "Error signing certificate.\n"));
		goto cleanup;
	}

	/*
	 * Store it on the token if the user asked for it.
	 */
	if (store) {
		numattr = 0;
		kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR,
		    &kstype, sizeof (kstype));
		numattr++;

		kmf_set_attr_at_index(attrlist, numattr, KMF_CERT_DATA_ATTR,
		    &outcert, sizeof (KMF_DATA));
		numattr++;

		if (outlabel != NULL) {
			kmf_set_attr_at_index(attrlist, numattr,
			    KMF_CERT_LABEL_ATTR,
			    outlabel, strlen(outlabel));
			numattr++;
		}

		if (kstype == KMF_KEYSTORE_NSS) {
			if (token != NULL)
				kmf_set_attr_at_index(attrlist, numattr,
				    KMF_TOKEN_LABEL_ATTR,
				    token, strlen(token));
			numattr++;
		}

		rv = kmf_store_cert(handle, numattr, attrlist);
		if (rv != KMF_OK) {
			display_error(handle, rv,
			    gettext("Failed to store cert "
			    "on PKCS#11 token.\n"));
			rv = KMF_OK;
			/* Not fatal, we can still write it to a file. */
		}
	}
	rv = kmf_create_cert_file(&outcert, fmt, certfile);

cleanup:
	kmf_free_signed_csr(&csrdata);
	kmf_free_data(&outcert);
	kmf_free_kmf_key(handle, &casignkey);

	return (rv);
}

/*
 * sign a CSR and generate an x509v3 certificate file.
 */
int
pk_signcsr(int argc, char *argv[])
{
	int			opt;
	extern int		optind_av;
	extern char		*optarg_av;
	char			*token_spec = NULL;
	char			*subject = NULL;
	char			*issuer = NULL;
	char			*dir = NULL;
	char			*prefix = NULL;
	char			*csrfile = NULL;
	char			*serstr = NULL;
	char			*ekustr = NULL;
	char			*kustr = NULL;
	char			*format = NULL;
	char			*storestr = NULL;
	char			*altname = NULL;
	char			*certfile = NULL;
	char			*lifetime = NULL;
	char			*signkey = NULL;
	char			*outlabel = NULL;
	uint32_t		ltime = 365 * 24 * 60 * 60; /* 1 Year */
	int			store = 0;
	uint16_t		kubits = 0;
	int			altcrit = 0, kucrit = 0;
	KMF_BIGINT		serial = { NULL, 0 };
	EKU_LIST		*ekulist = NULL;
	KMF_KEYSTORE_TYPE	kstype = 0;
	KMF_RETURN		rv = KMF_OK;
	KMF_HANDLE_T		kmfhandle = NULL;
	KMF_CREDENTIAL		tokencred = { NULL, 0 };
	KMF_GENERALNAMECHOICES	alttype = 0;
	KMF_ENCODE_FORMAT	fmt = KMF_FORMAT_PEM;

	/* Parse command line options.  Do NOT i18n/l10n. */
	while ((opt = getopt_av(argc, argv,
	    "k:(keystore)c:(csr)T:(token)d:(dir)"
	    "p:(prefix)S:(serial)s:(subject)a:(altname)"
	    "t:(store)F:(format)K:(keyusage)l:(signkey)"
	    "L:(lifetime)e:(eku)i:(issuer)"
	    "n:(outlabel)o:(outcert)")) != EOF) {
		if (EMPTYSTRING(optarg_av))
			return (PK_ERR_USAGE);
		switch (opt) {
			case 'k':
				if (kstype != 0)
					return (PK_ERR_USAGE);
				kstype = KS2Int(optarg_av);
				if (kstype == 0)
					return (PK_ERR_USAGE);
				break;
			case 't':
				if (storestr != NULL)
					return (PK_ERR_USAGE);
				storestr = optarg_av;
				store = yn_to_int(optarg_av);
				if (store == -1)
					return (PK_ERR_USAGE);
				break;
			case 'a':
				if (altname)
					return (PK_ERR_USAGE);
				altname = optarg_av;
				break;
			case 's':
				if (subject)
					return (PK_ERR_USAGE);
				subject = optarg_av;
				break;
			case 'i':
				if (issuer)
					return (PK_ERR_USAGE);
				issuer = optarg_av;
				break;
			case 'd':
				if (dir)
					return (PK_ERR_USAGE);
				dir = optarg_av;
				break;
			case 'p':
				if (prefix)
					return (PK_ERR_USAGE);
				prefix = optarg_av;
				break;
			case 'S':
				if (serstr != NULL)
					return (PK_ERR_USAGE);
				serstr = optarg_av;
				break;
			case 'c':
				if (csrfile)
					return (PK_ERR_USAGE);
				csrfile = optarg_av;
				break;
			case 'T':	/* token specifier */
				if (token_spec)
					return (PK_ERR_USAGE);
				token_spec = optarg_av;
				break;
			case 'l':	/* object with specific label */
				if (signkey)
					return (PK_ERR_USAGE);
				signkey = optarg_av;
				break;
			case 'e':
				if (ekustr != NULL)
					return (PK_ERR_USAGE);
				ekustr = optarg_av;
				break;
			case 'K':
				if (kustr != NULL)
					return (PK_ERR_USAGE);
				kustr = optarg_av;
				break;
			case 'F':
				if (format != NULL)
					return (PK_ERR_USAGE);
				format = optarg_av;
				break;
			case 'o':
				if (certfile != NULL)
					return (PK_ERR_USAGE);
				certfile = optarg_av;
				break;
			case 'L':
				if (lifetime != NULL)
					return (PK_ERR_USAGE);
				lifetime = optarg_av;
				break;
			case 'n':
				if (outlabel != NULL)
					return (PK_ERR_USAGE);
				outlabel = optarg_av;
				break;
			default:
				return (PK_ERR_USAGE);
		}
	}
	/* No additional args allowed. */
	argc -= optind_av;
	argv += optind_av;
	if (argc)
		return (PK_ERR_USAGE);


	/* Assume keystore = PKCS#11 if not specified. */
	if (kstype == 0)
		kstype = KMF_KEYSTORE_PK11TOKEN;

	DIR_OPTION_CHECK(kstype, dir);

	if (signkey == NULL) {
		(void) fprintf(stderr, gettext("The signing key label "
		    "or filename was not specified\n"));
		return (PK_ERR_USAGE);
	}
	if (csrfile == NULL) {
		(void) fprintf(stderr, gettext("The CSR filename was not"
		    " specified\n"));
		return (PK_ERR_USAGE);
	}
	if (certfile == NULL) {
		(void) fprintf(stderr, gettext("The output certificate file "
		    "was not specified\n"));
		return (PK_ERR_USAGE);
	}
	if (issuer == NULL) {
		(void) fprintf(stderr, gettext("The issuer DN "
		    "was not specified\n"));
		return (PK_ERR_USAGE);
	}
	if (lifetime != NULL) {
		if (Str2Lifetime(lifetime, &ltime) != 0) {
			cryptoerror(LOG_STDERR,
			    gettext("Error parsing lifetime string\n"));
			return (PK_ERR_USAGE);
		}
	}
	if (kstype == KMF_KEYSTORE_PK11TOKEN && EMPTYSTRING(token_spec)) {
		token_spec = PK_DEFAULT_PK11TOKEN;
	} else if (kstype == KMF_KEYSTORE_NSS && EMPTYSTRING(token_spec)) {
		token_spec = DEFAULT_NSS_TOKEN;
	}

	if (serstr != NULL) {
		uchar_t *bytes = NULL;
		size_t bytelen;

		rv = kmf_hexstr_to_bytes((uchar_t *)serstr, &bytes, &bytelen);
		if (rv != KMF_OK || bytes == NULL) {
			(void) fprintf(stderr, gettext("Serial number "
			    "must be specified as a hex number "
			    "(ex: 0x0102030405ffeeddee)\n"));
			return (PK_ERR_USAGE);
		}
		serial.val = bytes;
		serial.len = bytelen;
	} else {
		(void) fprintf(stderr, gettext("The serial number was not"
		    " specified\n"));
		return (PK_ERR_USAGE);
	}

	if ((kstype == KMF_KEYSTORE_PK11TOKEN ||
	    kstype == KMF_KEYSTORE_NSS)) {
		/* Need to get password for private key access */
		(void) get_token_password(kstype, token_spec,
		    &tokencred);
	}
	if (kustr != NULL) {
		rv = verify_keyusage(kustr, &kubits, &kucrit);
		if (rv != KMF_OK) {
			(void) fprintf(stderr, gettext("KeyUsage "
			    "must be specified as a comma-separated list. "
			    "See the man page for details.\n"));
			rv = PK_ERR_USAGE;
			goto end;
		}
	}
	if (ekustr != NULL) {
		rv = verify_ekunames(ekustr, &ekulist);
		if (rv != KMF_OK) {
			(void) fprintf(stderr, gettext("EKUs must "
			    "be specified as a comma-separated list. "
			    "See the man page for details.\n"));
			rv = PK_ERR_USAGE;
			goto end;
		}
	}
	if (altname != NULL) {
		char *p;
		rv = verify_altname(altname, &alttype, &altcrit);
		if (rv != KMF_OK) {
			(void) fprintf(stderr, gettext("Subject AltName "
			    "must be specified as a name=value pair. "
			    "See the man page for details.\n"));
			rv = PK_ERR_USAGE;
			goto end;
		}
		/* advance the altname past the '=' sign */
		p = strchr(altname, '=');
		if (p != NULL)
			altname = p + 1;
	}
	if (format && (fmt = Str2Format(format)) == KMF_FORMAT_UNDEF) {
		cryptoerror(LOG_STDERR,
		    gettext("Error parsing format string (%s).\n"),
		    format);
		return (PK_ERR_USAGE);
	}

	if ((rv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
		return (rv);
	}

	if (kstype == KMF_KEYSTORE_PK11TOKEN) {
		rv = pk_signcsr_pk11_nss(kmfhandle,
		    kstype, dir, prefix, token_spec, &tokencred,
		    signkey, csrfile, &serial, certfile, issuer, subject,
		    altname, alttype, altcrit, kubits, kucrit,
		    ekulist, ltime, fmt, store, outlabel);

	} else if (kstype == KMF_KEYSTORE_NSS) {
		if (dir == NULL)
			dir = PK_DEFAULT_DIRECTORY;

		rv = pk_signcsr_pk11_nss(kmfhandle,
		    kstype, dir, prefix, token_spec, &tokencred,
		    signkey, csrfile, &serial, certfile, issuer, subject,
		    altname, alttype, altcrit, kubits, kucrit,
		    ekulist, ltime, fmt, store, outlabel);

	} else if (kstype == KMF_KEYSTORE_OPENSSL) {
		rv = pk_signcsr_files(kmfhandle,
		    signkey, csrfile, &serial, certfile, issuer, subject,
		    altname, alttype, altcrit, kubits, kucrit,
		    ekulist, ltime, fmt);
	}

end:
	if (rv != KMF_OK) {
		display_error(kmfhandle, rv,
		    gettext("Error listing objects"));
	}

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

	if (tokencred.cred != NULL)
		free(tokencred.cred);

	free_eku_list(ekulist);

	(void) kmf_finalize(kmfhandle);
	return (rv);
}