/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This file implements the sign CSR operation for this tool. */ #include #include #include #include #include #include "common.h" #include #include #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 = {NULL, 0}; KMF_DATA rawcsr = {NULL, 0}; 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_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)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE_ATTR)); numattr++; /* cert data that is to be signed */ kmf_set_attr_at_index(attrlist, numattr, KMF_X509_CERTIFICATE_ATTR, cert, sizeof (KMF_X509_CERTIFICATE)); numattr++; /* output buffer for the signed cert */ kmf_set_attr_at_index(attrlist, numattr, KMF_CERT_DATA_ATTR, outdata, sizeof (KMF_DATA)); numattr++; 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 = {NULL, 0}; int numattr, count; 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, &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 = {NULL, 0}; KMF_CSR_DATA csrdata; KMF_KEY_HANDLE casignkey; KMF_KEY_CLASS keyclass = KMF_ASYM_PRI; int numattr = 0; int keys = 1; KMF_ATTRIBUTE attrlist[16]; KMF_X509_CERTIFICATE signedCert; boolean_t token_bool = B_TRUE; boolean_t private_bool = B_TRUE; 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, &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, <ime) != 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); }