export.c revision 7711facfe58561dd91d6ece0f5f41150c3956c83
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file implements the export operation for this tool.
* The basic flow of the process is to find the soft token,
* log into it, find the PKCS#11 objects in the soft token
* to be exported matching keys with their certificates, export
* them to the PKCS#12 file encrypting them with a file password
* if desired, and log out.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <cryptoutil.h>
#include <security/cryptoki.h>
#include "common.h"
#include "biginteger.h"
#include "osslcommon.h"
#include "p12common.h"
/*
* Writes OpenSSL objects to PKCS#12 file. The PKCS#11 objects from
* the soft token need to be converted to OpenSSL structures prior
* to this call, since the PKCS#12 routines depend on that format.
* This code is patterned from OpenSSL apps that write PKCS#12 files.
*
* Note: it's not clear from the usage of all the functions here by
* OpenSSL apps whether these functions have return values or error
* conditions that can be checked. This function may benefit from
* a closer review at a later time.
*/
static int
/* ARGSUSED */
{
int lab_len = 0;
int i;
int n_writes = 0;
cryptodebug("inside write_objs_pkcs12");
/* Do not reset *successes or *failures -- keep running totals. */
/* If there is nothing to write to the PKCS#12 file, leave. */
cryptodebug("nothing to write to export file");
return (0);
}
/*
* Section 1:
*
* The first PKCS#12 container (safebag) will hold the certificates
* associated with this key. The result of this section is a
* PIN-encrypted PKCS#7 container (authsafe). If there are no
* certificates, there is no point in creating the "safebag" or the
* "authsafe" so we go to the next section.
*/
/* Start a PKCS#12 safebag container for the certificates. */
cryptodebug("creating certificate PKCS#12 safebag");
"Unable to create PKCS#12 certificate bag."));
(*failures)++;
return (-1);
}
/* Add the cert corresponding to private key to bag_stack. */
if (cert) {
/* Convert cert from X509 struct to PKCS#12 bag */
cryptodebug("adding certificate to PKCS#12 safebag");
"Unable to convert certificate to "
"PKCS#12 bag."));
/* Cleanup the safebag. */
(*failures)++;
return (-1);
}
/* Add the key id to the certificate bag. */
cryptodebug("add key id to PKCS#12 safebag");
cryptodebug("error not caught");
/* Add the friendly name to the certificate bag. */
"label PKCS#12 safebag with friendly name");
lab_len))
cryptodebug("error not caught");
}
/* Pile it on the bag_stack. */
cryptodebug("error not caught");
n_writes++;
}
/* Add all the CA chain certs to the bag_stack. */
if (ca_certs) {
cryptodebug("adding CA certificate chain to PKCS#12 "
"safebag");
/*
* Go through the stack of CA certs, converting each
* one to a PKCS#12 bag and piling them onto the
* bag_stack.
*/
for (i = 0; i < sk_X509_num(ca_certs); i++) {
/*
* sk_X509_value() is macro that embeds a
* cast to (X509 *). Here it translates
* into ((X509 *)sk_value((ca_certs), (i))).
* Lint is complaining about the embedded
* casting, and to fix it, you need to fix
* openssl header files.
*/
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* Convert CA cert to PKCS#12 bag. */
cryptodebug("adding CA certificate #%d "
"to PKCS#12 safebag", i+1);
"Unable to convert CA certificate "
"#%d to PKCS#12 bag."), i+1);
/* Cleanup the safebag. */
(*failures)++;
return (-1);
}
/* Note CA certs do not have friendly name. */
/* Pile it onto the bag_stack. */
cryptodebug("error not caught");
n_writes++;
}
}
/* Turn bag_stack of certs into encrypted authsafe. */
cryptodebug("encrypt certificate PKCS#12 bag into "
"PKCS#7 authsafe");
0, PKCS12_DEFAULT_ITER, bag_stack);
/* Clear away this bag_stack, we're done with it. */
if (cert_authsafe == NULL) {
"Unable to PKCS#7-encrypt certificate bag."));
(*failures)++;
return (-1);
}
}
/*
* Section 2:
*
* The second PKCS#12 container (safebag) will hold the private key
* that goes with the certificates above. The results of this section
* is an unencrypted PKCS#7 container (authsafe). If there is no
* private key, there is no point in creating the "safebag" or the
* "authsafe" so we go to the next section.
*/
/* Make a PKCS#8 shrouded key bag. */
cryptodebug("create PKCS#8 shrouded key out of private key");
"Unable to create PKCS#8 shrouded key for "
"private key."));
(*failures)++;
return (-1);
}
/* Put the shrouded key into a PKCS#12 bag. */
cryptodebug("convert shrouded key to PKCS#12 bag");
NID_pbe_WithSHA1And3_Key_TripleDES_CBC, (char *)pin,
/* Clean up the PKCS#8 shrouded key, don't need it now. */
"Unable to convert private key to PKCS#12 bag."));
(*failures)++;
return (-1);
}
/* Add the key id to the certificate bag. */
cryptodebug("add key id to PKCS#12 safebag");
cryptodebug("error not caught");
/* Add the cert friendly name to the private key bag. */
cryptodebug("label PKCS#12 safebag with friendly name");
cryptodebug("error not caught");
}
/* Start a PKCS#12 safebag container for the private key. */
cryptodebug("creating private key PKCS#12 safebag");
"Unable to create PKCS#12 private key bag."));
(*failures)++;
return (-1);
}
/* Pile on the private key on the bag_stack. */
cryptodebug("error not caught");
/* Turn bag_stack with private key into unencrypted authsafe. */
cryptodebug("put private PKCS#12 bag into PKCS#7 authsafe");
/* Clear away this bag_stack, we're done with it. */
if (key_authsafe == NULL) {
"Unable to PKCS#7-convert private key bag."));
(*failures)++;
return (-1);
}
n_writes++;
}
/*
* Section 3:
*
* This is where the two PKCS#7 containers, one for the certificates
* and one for the private key, are put together into a PKCS#12
* element. This final PKCS#12 element is written to the export file.
*/
/* Start a PKCS#7 stack. */
cryptodebug("create PKCS#7 authsafe for private key and certificates");
if (authsafe_stack == NULL) {
"Unable to create PKCS#7 container for private key "
"and certificates."));
(*failures)++;
return (-1);
}
/* Put certificates and private key into PKCS#7 stack. */
if (key_authsafe != NULL) {
cryptodebug("put private key authsafe into PKCS#7 container");
cryptodebug("error not caught");
}
if (cert_authsafe != NULL) {
cryptodebug("put certificate authsafe into PKCS#7 container");
cryptodebug("error not caught");
}
/* Create PKCS#12 element out of PKCS#7 stack. */
cryptodebug("create PKCS#12 element for export file");
"Unable to create PKCS#12 element for export file."));
(*failures)++;
return (-1);
}
/* Put the PKCS#7 stack into the PKCS#12 element. */
cryptodebug("error not caught");
/* Clear away the PKCS#7 stack, we're done with it. */
/* Set the integrity MAC on the PKCS#12 element. */
cryptodebug("setting MAC for PKCS#12 element");
cryptodebug("error not caught");
/* Write the PKCS#12 element to the export file. */
cryptodebug("writing PKCS#12 element to export file");
cryptodebug("error not caught");
/* Clear away the PKCS#12 element. */
return (0);
}
/*
* Get token objects: private key, its cert, and its cert chain.
*/
static CK_RV
{
};
};
cryptodebug("inside get_token_objs");
/* Get the size of the object's CKA_ID field first. */
return (rv);
}
/* Allocate the space needed for the key id. */
return (CKR_HOST_MEMORY);
}
/* Get the CKA_ID field to match obj with its cert. */
return (rv);
}
/* Now try to find any certs that have the same id. */
cryptodebug("searching for certificates with same CKA_ID");
return (rv);
}
/* Find the first cert that matches the key id. */
return (rv);
}
(void) C_FindObjectsFinal(sess);
/* We currently do not find all the certs in the chain. */
*chain_len = 0;
return (CKR_OK);
}
/*
* Converts PKCS#11 biginteger_t format to OpenSSL BIGNUM.
* "to" should be the address of a ptr init'ed to NULL to
* receive the BIGNUM, e.g.,
* biginteger_t from;
* BIGNUM *foo = NULL;
* cvt_bigint2bn(&from, &foo);
*/
static int
{
cryptodebug("inside cvt_bigint2bn");
return (-1);
cryptodebug("calling BN_bin2bn");
NULL)
return (-1);
return (0);
}
/*
* Convert PKCS#11 RSA private key to OpenSSL EVP_PKEY structure.
*/
static CK_RV
{
{ CKA_MODULUS, NULL, 0 },
{ CKA_PUBLIC_EXPONENT, NULL, 0 },
};
int i;
cryptodebug("inside cvt_rsa2evp_pkey");
cryptodebug("calling RSA_new");
"Unable to allocate internal RSA structure."));
return (CKR_HOST_MEMORY);
}
/* Get the sizes of the attributes we need. */
cryptodebug("calling C_GetAttributeValue for size info");
CKR_OK) {
"Unable to get RSA private key attribute sizes (%s)."),
return (rv);
}
/* Allocate memory for each attribute. */
for (i = 0; i < count; i++) {
rsa_pri_attrs[i].ulValueLen == 0) {
cryptodebug("cvt_rsa2evp_pkey: *** should not happen");
rsa_pri_attrs[i].ulValueLen = 0;
continue;
}
if ((rsa_pri_attrs[i].pValue =
return (CKR_HOST_MEMORY);
}
}
/* Now really get the attributes. */
cryptodebug("calling C_GetAttributeValue for attribute info");
CKR_OK) {
"Unable to get RSA private key attributes (%s)."),
return (rv);
}
/*
* Fill in all the temp variables. Modulus and public exponent
* are required. The rest are optional.
*/
i = 0;
rsa_pri_attrs[i].ulValueLen != 0)
i++;
rsa_pri_attrs[i].ulValueLen != 0)
i++;
rsa_pri_attrs[i].ulValueLen != 0)
i++;
rsa_pri_attrs[i].ulValueLen != 0)
i++;
rsa_pri_attrs[i].ulValueLen != 0)
i++;
rsa_pri_attrs[i].ulValueLen != 0)
i++;
/* Start the conversion to internal OpenSSL RSA structure. */
/* Modulus n */
"Unable to convert RSA private key modulus."));
return (CKR_GENERAL_ERROR);
}
/* Public exponent e */
"Unable to convert RSA private key public exponent."));
return (CKR_GENERAL_ERROR);
}
/* Private exponent e */
"RSA private key private exponent."));
return (CKR_GENERAL_ERROR);
}
} else
cryptodebug("no RSA private key private exponent");
/* Prime p */
"Unable to convert RSA private key prime 1."));
return (CKR_GENERAL_ERROR);
}
} else
cryptodebug("no RSA private key prime 1");
/* Prime q */
"Unable to convert RSA private key prime 2."));
return (CKR_GENERAL_ERROR);
}
} else
cryptodebug("no RSA private key prime 2");
/* Private exponent d modulo p-1 */
"Unable to convert RSA private key exponent 1."));
return (CKR_GENERAL_ERROR);
}
} else
cryptodebug("no RSA private key exponent 1");
/* Private exponent d modulo q-1 */
"Unable to convert RSA private key exponent 2."));
return (CKR_GENERAL_ERROR);
}
} else
cryptodebug("no RSA private key exponent 2");
/* CRT coefficient q-inverse mod p */
"Unable to convert RSA private key coefficient."));
return (CKR_GENERAL_ERROR);
}
} else
cryptodebug("no RSA private key coefficient");
/* Create OpenSSL EVP_PKEY struct in which to stuff RSA struct. */
cryptodebug("calling EVP_PKEY_new");
"Unable to allocate internal EVP_PKEY structure."));
return (CKR_HOST_MEMORY);
}
/* Put the RSA struct into the EVP_PKEY struct and return it. */
cryptodebug("calling EVP_PKEY_set1_RSA");
return (CKR_OK);
}
/*
* Convert PKCS#11 DSA private key to OpenSSL EVP_PKEY structure.
*/
static CK_RV
{
{ CKA_SUBPRIME, NULL, 0 },
};
int i;
cryptodebug("inside cvt_dsa2evp_pkey");
cryptodebug("calling DSA_new");
"Unable to allocate internal DSA structure."));
return (CKR_HOST_MEMORY);
}
/* Get the sizes of the attributes we need. */
cryptodebug("calling C_GetAttributeValue for size info");
CKR_OK) {
"Unable to get DSA private key object attributes (%s)."),
return (rv);
}
/* Allocate memory for each attribute. */
for (i = 0; i < count; i++) {
dsa_pri_attrs[i].ulValueLen == 0) {
cryptodebug("cvt_dsa2evp_pkey: *** should not happen");
dsa_pri_attrs[i].ulValueLen = 0;
continue;
}
if ((dsa_pri_attrs[i].pValue =
return (CKR_HOST_MEMORY);
}
}
/* Now really get the attributes. */
cryptodebug("calling C_GetAttributeValue for attribute info");
CKR_OK) {
"Unable to get DSA private key attributes (%s)."),
return (rv);
}
/* Fill in all the temp variables. They are all required. */
i = 0;
/* Start the conversion to internal OpenSSL DSA structure. */
/* Prime p */
"Unable to convert DSA private key prime."));
return (CKR_GENERAL_ERROR);
}
/* Subprime q */
"Unable to convert DSA private key subprime."));
return (CKR_GENERAL_ERROR);
}
/* Base g */
"Unable to convert DSA private key base."));
return (CKR_GENERAL_ERROR);
}
/* Private key x */
"Unable to convert DSA private key value."));
return (CKR_GENERAL_ERROR);
}
/* Create OpenSSL EVP PKEY struct in which to stuff DSA struct. */
cryptodebug("calling EVP_PKEY_new");
"Unable to allocate internal EVP_PKEY structure."));
return (CKR_HOST_MEMORY);
}
/* Put the DSA struct into the EVP_PKEY struct and return it. */
cryptodebug("calling EVP_PKEY_set1_DSA");
return (CKR_OK);
}
/*
* Convert PKCS#11 DH private key to OpenSSL EVP_PKEY structure.
*/
static CK_RV
{
};
int i;
cryptodebug("inside cvt_dh2evp_pkey");
cryptodebug("calling DH_new");
"Unable to allocate internal DH structure."));
return (CKR_HOST_MEMORY);
}
/* Get the sizes of the attributes we need. */
cryptodebug("calling C_GetAttributeValue for size info");
CKR_OK) {
"Unable to get DH private key object attributes (%s)."),
return (rv);
}
/* Allocate memory for each attribute. */
for (i = 0; i < count; i++) {
dh_pri_attrs[i].ulValueLen == 0) {
cryptodebug("cvt_dh2evp_pkey: ***should not happen");
dh_pri_attrs[i].ulValueLen = 0;
continue;
}
if ((dh_pri_attrs[i].pValue =
return (CKR_HOST_MEMORY);
}
}
/* Now really get the attributes. */
cryptodebug("calling C_GetAttributeValue for attribute info");
CKR_OK) {
"Unable to get DH private key attributes (%s)."),
return (rv);
}
/* Fill in all the temp variables. They are all required. */
i = 0;
/* Start the conversion to internal OpenSSL DH structure. */
/* Prime p */
"Unable to convert DH private key prime."));
return (CKR_GENERAL_ERROR);
}
/* Base g */
"Unable to convert DH private key base."));
return (CKR_GENERAL_ERROR);
}
/* Private value x */
"Unable to convert DH private key value."));
return (CKR_GENERAL_ERROR);
}
/* Create OpenSSL EVP PKEY struct in which to stuff DH struct. */
cryptodebug("calling EVP_PKEY_new");
"Unable to allocate internal EVP_PKEY structure."));
return (CKR_HOST_MEMORY);
}
/* Put the DH struct into the EVP_PKEY struct and return it. */
cryptodebug("calling EVP_PKEY_set1_DH");
return (CKR_OK);
}
/*
* Convert PKCS#11 private key object to OpenSSL EVP_PKEY structure.
*/
static CK_RV
{
static CK_KEY_TYPE keytype = 0;
};
cryptodebug("inside cvt_obj2evp_pkey");
/* Find out the key type to do the right conversion. */
cryptodebug("calling C_GetAttributeValue");
CKR_OK) {
"Unable to get token object key type (%s)."),
return (rv);
}
switch (keytype) {
case CKK_RSA:
cryptodebug("converting RSA key");
case CKK_DSA:
cryptodebug("converting DSA key");
case CKK_DH:
cryptodebug("converting DH key");
default:
"Private key type 0x%02x conversion not supported."),
keytype);
return (CKR_GENERAL_ERROR);
}
}
/*
* Convert PKCS#11 certificate object to OpenSSL X509 structure.
*/
static CK_RV
{
CK_ULONG subject_len = 0;
CK_ULONG issuer_len = 0;
CK_ULONG serial_len = 0;
};
int i = 0;
cryptodebug("inside cvt_cert2x509");
cryptodebug("calling X509_new");
"Unable to allocate internal X509 structure."));
return (CKR_HOST_MEMORY);
}
/* Get the sizes of the attributes we need. */
cryptodebug("calling C_GetAttributeValue for size info");
CKR_OK) {
"Unable to get certificate attribute sizes (%s)."),
return (rv);
}
/* Allocate memory for each attribute. */
for (i = 0; i < count; i++) {
cert_attrs[i].ulValueLen == 0) {
cryptodebug("cvt_cert2x509: *** should not happen");
cert_attrs[i].ulValueLen = 0;
continue;
}
== NULL) {
return (CKR_HOST_MEMORY);
}
}
/* Now really get the attributes. */
cryptodebug("calling C_GetAttributeValue for attribute info");
CKR_OK) {
"Unable to get certificate attributes (%s)."),
return (rv);
}
/*
* Fill in all the temp variables. Subject and value are required.
* The rest are optional.
*/
i = 0;
cert_attrs[i].ulValueLen != 0)
i++;
cert_attrs[i].ulValueLen != 0)
i++;
cert_attrs[i].ulValueLen != 0)
i++;
cert_attrs[i].ulValueLen != 0)
i++;
/* Start the conversion to internal OpenSSL X509 structure. */
/* Subject name (required) */
cryptodebug("calling d2i_X509_NAME for subject name");
NULL) {
"Unable to convert certificate subject name."));
return (CKR_GENERAL_ERROR);
}
cryptodebug("calling X509_set_subject_name");
"Unable to pack certificate subject name entries."));
return (CKR_GENERAL_ERROR);
}
/* Label (optional) */
cryptodebug("calling X509_alias_set1");
cryptodebug("error not caught");
/* Id (optional) */
cryptodebug("calling X509_keyid_set1");
cryptodebug("error not caught");
/* Issuer name (optional) */
cryptodebug("calling d2i_X509_NAME for issuer name");
"Unable to convert certificate issuer name."));
return (CKR_GENERAL_ERROR);
}
cryptodebug("calling X509_set_issuer_name");
"Unable to pack certificate issuer name entries."));
return (CKR_GENERAL_ERROR);
}
/* Serial number (optional) */
cryptodebug("calling c2i_ASN1_INTEGER for serial number");
NULL) {
"Unable to convert certificate serial number."));
return (CKR_GENERAL_ERROR);
}
cryptodebug("calling X509_set_serialNumber");
cryptodebug("error not caught");
/*
* Value (required)
*
* The rest of this code takes the CKA_VALUE attribute, converts
* it into a temp OpenSSL X509 structure and picks out the rest
* of the fields we need to convert it back into the current X509
* structure that will get exported. The reason we don't just
* start with CKA_VALUE is because while the object was in the
* softtoken, it is possible that some of its attributes changed.
* Those changes would not appear in CKA_VALUE and would be lost
* if we started with CKA_VALUE that was saved originally.
*/
cryptodebug("calling d2i_X509 for cert value");
"Unable to convert main certificate values."));
return (CKR_GENERAL_ERROR);
}
/* Transfer these values from temp_cert to cert. */
cryptodebug("calling X509_set_version/X509_get_version");
cryptodebug("error not caught");
cryptodebug("calling X509_set_notBefore/X509_get_notBefore");
cryptodebug("error not caught");
cryptodebug("calling X509_set_notAfter/X509_get_notAfter");
cryptodebug("error not caught");
cryptodebug("calling X509_set_pubkey/X509_get_pubkey");
cryptodebug("error not caught");
/*
* These don't get transfered from temp_cert to cert.
* It -appears- that they may get regenerated as needed.
*
* cert->cert_info->signature = dup(temp_cert->cert_info->signature);
* cert->sig_alg = dup(temp_cert->sig_alg);
* cert->signature = dup(temp_cert->signature);
* cert->skid = dup(temp_cert->skid);
* cert->akid = dup(temp_cert->akid);
*/
*c = cert;
return (CKR_OK);
}
static CK_RV
{
int i;
cryptodebug("inside convert_token_objs");
return (rv);
cryptodebug("converting cert corresponding to private key");
return (rv);
}
if (chain_len != 0) {
cryptodebug("converting ca chain of %d certs corresponding "
"to private key", chain_len);
ch = sk_X509_new_null();
for (i = 0; i < chain_len; i++) {
CKR_OK) {
return (rv);
}
cryptodebug("error not caught");
}
}
return (CKR_OK);
}
/*
* Export objects from token to PKCS#12 file.
*/
int
{
char *token_name = NULL;
char full_name[FULL_NAME_LEN];
CK_ULONG pk12pinlen = 0;
int i = 0;
cryptodebug("inside pk_export");
/* Get rid of subcommand work "export". */
argc--;
argv++;
/* One additional arg required: filename. */
if (argc != 1)
return (PK_ERR_USAGE);
/* Done parsing command line options. */
/* Check if the file exists and might be overwritten. */
"will be overwritten."), filename);
return (0);
}
}
/* Export operation only supported on softtoken. */
if (token_name == NULL)
/* Find the slot with token. */
"Unable to find token %s (%s)."), full_name,
return (PK_ERR_PK11);
}
/* Get the user's PIN. */
"Unable to get token passphrase (%s)."),
return (PK_ERR_PK11);
}
/* Assume user must be logged in R/W to export objects from token. */
CKR_OK) {
gettext("Unable to log into token (%s)."),
return (PK_ERR_PK11);
}
/* Collect all private keys first. */
"Unable to retrieve private key token objects (%s)."),
return (PK_ERR_PK11);
}
/* Nothing to do? */
if (num_objs == 0) {
return (0);
}
/* Setup OpenSSL context. */
/* Create PKCS#12 file. */
return (PK_ERR_SYSTEM);
}
/* Get the PIN for the PKCS#12 export file. */
CKR_OK) {
gettext("Unable to get export file passphrase (%s)."),
return (PK_ERR_PK11);
}
for (i = 0; i < num_objs; i++) {
/* Get a private key and its certificate and CA chain. */
/*
* Note this "rv" is either CKR_OK or !CKR_OK. The
* read_token_objs().
*/
gettext("Unable to get token objects."));
return (PK_ERR_PK11);
}
/* Convert to OpenSSL equivalents. */
/*
* Note this "rv" is either CKR_OK or !CKR_OK. The
* read_token_objs().
*/
gettext("Unable to convert token objects."));
return (PK_ERR_PK11);
}
/*
* When exporting of cert chains is implemented, these
* messages should be updated accordingly.
*/
"Writing object #%d...\n"), i+1);
else
"and its certificate...\n"), i+1);
/* Write object and its certs to the PKCS#12 export file. */
"Unable to write object #%d to export file."), i+1);
return (PK_ERR_OPENSSL);
}
/* Destroy key id and CA cert chain, done with them. */
}
"%d token objects exported, %d errors occurred.\n"),
/* Close PKCS#12 file. */
/* Clean up. */
return (0);
}