The Oracle Solaris Key Management Framework (KMF) provides a unified set of interfaces for managing Public Key Infrastructure (PKI) objects in Oracle Solaris. These interfaces include both programming interfaces and administrative tools.
This chapter discusses the following topics:
Developers and system administrators can choose among several different keystore systems when designing systems that employ PKI technologies. A keystore is a storage system for PKI objects. The primary choices for Oracle Solaris users are NSS, OpenSSL, and PKCS#11. Each of these keystore systems presents different programming interfaces and administrative tools. None of these keystore systems includes any PKI policy enforcement system.
KMF provides generic interfaces that manipulate keys and certificates in all of these keystores.
A generic API layer enables the developer to specify which type of keystore to use. KMF also provides plugin modules for each of these three keystore systems so that you can write new applications to use any of these keystores. Applications written to KMF are not bound to one keystore system.
A management utility enables the administrator to manage PKI objects in all three of these keystores. You do not need to use a different utility for each keystore.
KMF also provides a system-wide policy database that KMF applications can use, regardless of which type of keystore is being used. The administrator can create policy definitions in a global database. KMF applications can choose which policy to assert, and then all subsequent KMF operations behave according to the limitations of that policy. Policy definitions include rules for how to perform validations, requirements for key usage and extended key usage, trust anchor definitions, Online Certificate Status Protocol (OCSP) parameters, and Certificate Revocation List (CRL) DB parameters such as location.
Oracle Solaris KMF includes the following features:
Programming interfaces for developing PKI aware applications. These interfaces are keystore independent: The interface does not bind the application to a particular keystore system such as NSS, OpenSSL, or PKCS#11.
An administrative utility for managing PKI objects.
A PKI policy database and enforcement system for PKI aware applications. The enforcement system is keystore independent and can be applied system-wide.
A plugin interface to extend KMF for legacy and proprietary systems.
KMF consumers include any project that uses certificates, such as authentication services and smart card authentication with X.509 certificates.
This section describes the following KMF components:
The pktool(1) key management tool
The KMF policy database
The kmfcfg(1) policy definition and plugin configuration utility
KMF data types defined in kmftypes.h and programming interfaces defined in kmfapi.h and libkmf(3LIB)
The following pktool(1) subcommands specifically support KMF:
Delete objects in the keystore.
Download a CRL or certificate file from an external source.
Export objects from the keystore to a file.
Create a self-signed X.509v3 certificate.
Create a PKCS#10 Certificate Signing Request (CSR) file.
Create a symmetric key in the keystore.
Displays a help message.
Import objects from an external source.
Initialize a PKCS#11 token.
List a summary of objects in the keystore.
Change user authentication passphrase for keystore access.
Sign a PKCS#10 CSR.
List all visible PKCS#11 tokens.
KMF policy is a hierarchical tree of policies. A default policy is defined when the system is installed. The default policy applies unless the application asserts a different policy.
Policy parameters control the use of X.509 certificates by an application. KMF policy applies to all certificates and is not restricted to any particular keystore.
Use the kmfcfg(1) utility to manage the KMF policy database and configure plugins. You can use kmfcfg to list, create, modify, delete, import, and export policy definitions in the system default database file /etc/security/kmfpolicy.xml or in a user-defined database file. Note that you cannot modify the default policy in the system KMF policy database. For plugin configuration, you can use kmfcfg to display plugin information, install or uninstall a KMF plugin, and modify the plugin option.
The following list shows some of the KMF policy attributes. See the kmfcfg(1) man page for a complete list and descriptions of these policy attributes.
Policy Name. Applications reference this name.
Default Keystore. Examples include NSS, files, PKCS11.
Ignore Date. Ignore the validity periods defined in the certificates when evaluating their validity.
Ignore Unknown EKU. Ignore any unrecognized EKU values in the Extended Key Usage extension.
Token Label. This attribute only applies to NSS or PKCS11 keystores.
Validation Method. Examples include OCSP and CRL.
Key Usage Values. This attribute is a comma separated list of key usage values that are required by the policy being defined. These bits must be set in order to use the certificate.
Extended Key Usage Values. This attribute is a comma separated list of Extended Key Usage OIDs that are required by the policy being defined. These OIDS must be present in order to use the certificate.
See the kmfpolicy.h file for definitions of policy data types.
The following plugin libraries are provided in Oracle Solaris KMF:
PKCS#11 keystore plugin: kmf_pkcs11
OpenSSL keystore plugin: kmf_openssl
NSS keystore plugin: kmf_nss
The Oracle Solaris KMF provides abstract APIs for PKI operations. Applications written to KMF can access multiple keystores such as files (OpenSSL), NSS, and PKCS11 tokens and multiple validation modules such as OCSP and CRL checking. The KMF API can be extended by third parties for proprietary and legacy implementations.
The KMF APIs are provided in the Key Management Framework Library, libkmf(3LIB). These APIs enable your application to create and manage public key objects such as public/private keypairs, certificates, CSRs, certificate validation, CRLs, and OCSP response processing.
Keys, certificate, and CSR operations: create and delete, store and retrieve, search, import and export
Common cryptographic operations: sign and verify, encrypt and decrypt using certificates as keys
Access complex PKI objects: set and get X.509 attributes and extensions, and extract data in human-readable formats
The KMF APIs are defined in the kmfapi.h file, and structures and types are defined in the kmftypes.h file. The kmfapi.h file lists the functions in the following groups:
Setup operations
Key operations
Certificate operations
Cryptographic operations with key or certificate
CRL operations
CSR operations
Get certificate operations
Set certificate operations
PK12 operations
OCSP operations
Policy operations
Error handling
Memory cleanup operations
APIs for PKCS#11 tokens
Attribute management operations
The pktool application is an excellent example of how to use the KMF APIs.
This section shows a simple application that uses KMF. This section describes the basic steps that an application needs to take in order to perform some KMF operations. This example assumes that you have experience in C programming and a basic understanding of public key technologies and standards. This example goes through the steps of initializing KMF for use and then creates a self-signed X.509v3 certificate and associated RSA key pair. This example also shows how to use the KMF-enhanced pktool command to verify that the application was successful.
To give the program access to the KMF function prototypes and type definitions, include the kmfapi.h file.
#include <stdio.h> #include <kmfapi.h>
Be sure to include the KMF library in the link step.
$ cc -o kmftest kmftest.c -lkmf |
See the kmftypes.h file for definitions of structures and types. This example uses variables of the following KMF types.
Session handle for KMF calls
Return code for all KMF calls
Handle to a KMF key
KMF credential
Make sure this is big enough
Keystore type, such as KMF_KEYSTORE_PK11TOKEN
Key type, such as KMF_RSA
Data record that gets signed
Distinguished name record
Final certificate data record
Variable length integer
The user can verify that the program successfully created the certificate and keypair by using the pktool(1M) utility.
$ pktool list objtype=both Enter pin for Sun Software PKCS#11 softtoken : Found 1 certificates. 1. (X.509 certificate) Label: admin@foobar.com ID: 09:ac:7f:1a:01:f7:fc:a9:1a:cd:fd:8f:d4:92:4c:25:bf:b1:97:fe Subject: C=US, ST=CA, L=Menlo Park, O=Foobar Inc., OU=Foobar IT Office, CN=admin@foobar.com Issuer: C=US, ST=CA, L=Menlo Park, O=Foobar Inc., OU=Foobar IT Office, CN=admin@foobar.com Serial: 0x452BF693 X509v3 Subject Alternative Name: email:admin@foobar.com Found 1 keys. Key #1 - RSA private key: admin@foobar.com |
See the libkmf(3LIB) man page for definitions of KMF APIs.
This application performs the following steps:
Before any KMF functions can be called, the application must first use kmf_initialize() to initialize a handle for a KMF session. This handle is used as the first argument to most KMF function calls. It is an opaque data type and is used to hold internal state and context information for that session.
This example application uses the PKCS#11 keystore. Use kmf_configure_keystore() to define a token to use for future operations.
The first step to create a certificate or a PKCS#10 CSR is to generate a keypair. Use kmf_create_keypair() to create both the public and private keys needed and store the private key in the specified keystore. The function returns handles to the application so that the caller can reference the public and private key objects in future operations if necessary.
Once a keypair is established, use kmf_set_cert_pubkey() and kmf_set_cert_version() to populate the template record that is used to generate the final certificate. KMF provides different APIs for setting the various fields of an X.509v3 certificate, including extensions. Use kmf_hexstr_to_bytes(), kmf_set_cert_serial(), kmf_set_cert_validity(), and kmf_set_cert_sig_alg() to set the serial number. The serial number is a KMF_BIGINT record. Use kmf_dn_parser(), kmf_set_cert_subject(), and kmf_set_cert_issuer() to create a KMF_X509_NAME structure.
Because this is a self-signed certificate creation exercise, this application signs the certificate template created above with the private key that goes with the public key in the certificate itself. This kmf_sign_cert() operation results in a KMF_DATA record that contains the ASN.1 encoded X.509v3 certificate data.
Now that the certificate is signed and in its final format, it can be stored in any of the keystores. Use kmf_store_cert() to store the certificate in the PKCS#11 token defined at the beginning of this application. The certificate could also be stored in NSS or an OpenSSL file at this point.
Memory allocated to data structures generated by KMF should be cleaned up when the data structure is no longer needed. KMF provides convenience APIs for properly deallocating memory associated with these objects. The proper cleanup of memory is strongly encouraged in order to conserve resources. Cleanup interfaces include kmf_free_data(), kmf_free_dn(), and kmf_finalize().
Below is the complete source code for this example application, including all of the data types and helper functions. When you compile, be sure to include the KMF library.
/* * KMF Example code for generating a self-signed X.509 certificate. * This is completely unsupported and is just to be used as an example. * * Compile: * $ cc -o keytest keytest.c -lkmf * * Run: * $ ./keytest * * Once complete, the results can be verified using the pktool(1) command: * * $ pktool list * This should show an RSA public key labeled "keytest" and a cert labeled "keytest". * * The objects created by this program can be deleted from the keystore * using pktool(1) also: * * $ pktool delete label=keytest * */ #include <stdio.h> #include <strings.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <tzfile.h> #include <kmfapi.h> int main(int argc, char *argv[]) { KMF_HANDLE_T kmfhandle; KMF_RETURN ret; char opt, *str = NULL; extern char *optarg; KMF_KEY_HANDLE prikey, pubkey; KMF_CREDENTIAL cred; KMF_ATTRIBUTE attrlist[16]; /* this needs to be big enough */ KMF_KEYSTORE_TYPE kstype; KMF_KEY_ALG keytype; KMF_KEY_HANDLE prik, pubk; KMF_X509_CERTIFICATE certstruct; KMF_X509_NAME certsubject, certissuer; KMF_DATA rawcert; KMF_BIGINT serno; char *token = "Sun Software PKCS#11 softtoken"; char *keylabel = "keytest"; boolean_t readonly = B_FALSE; uint32_t keylen = 1024; uint32_t ltime = SECSPERDAY * DAYSPERNYEAR; /* seconds in a year (see tzfile.h) */ char prompt[1024]; int numattrs; (void) memset(&certstruct, 0, sizeof (certstruct)); (void) memset(&rawcert, 0, sizeof (rawcert)); (void) memset(&certissuer, 0, sizeof (certissuer)); (void) memset(&certsubject, 0, sizeof (certsubject)); /* * Initialize a KMF handle for use in future calls. */ ret = kmf_initialize(&kmfhandle, NULL, NULL); if (ret != KMF_OK) { printf("kmf_initialize failed: 0x%0x\n", ret); exit(1); } /* We want to use the PKCS11 keystore */ kstype = KMF_KEYSTORE_PK11TOKEN; numattrs = 0; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); numattrs++; /* Indicate which PKCS11 token will be used */ kmf_set_attr_at_index(attrlist, numattrs, KMF_TOKEN_LABEL_ATTR, token, strlen(token)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_READONLY_ATTR, &readonly, sizeof (readonly)); numattrs++; ret = kmf_configure_keystore(kmfhandle, numattrs, attrlist); if (ret != KMF_OK) exit (ret); /* Reset the attribute count for a new command */ numattrs = 0; /* * Get the PIN to access the token. */ (void) snprintf(prompt, sizeof (prompt), "Enter PIN for %s:", token); cred.cred = getpassphrase(prompt); if (cred.cred != NULL) { cred.credlen = strlen(cred.cred); kmf_set_attr_at_index(attrlist, numattrs, KMF_CREDENTIAL_ATTR, &cred, sizeof (cred)); numattrs++; } kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); numattrs++; keytype = KMF_RSA; keylen = 1024; keylabel = "keytest"; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYALG_ATTR, &keytype, sizeof (keytype)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYLENGTH_ATTR, &keylen, sizeof (keylen)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYLABEL_ATTR, keylabel, strlen(keylabel)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_CREDENTIAL_ATTR, &cred, sizeof (cred)); numattrs++; /* * Set the handles so they can be used later. */ kmf_set_attr_at_index(attrlist, numattrs, KMF_PRIVKEY_HANDLE_ATTR, &prik, sizeof (prik)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_PUBKEY_HANDLE_ATTR, &pubk, sizeof (pubk)); numattrs++; ret = kmf_create_keypair(kmfhandle, numattrs, attrlist); if (ret != KMF_OK) { printf("kmf_create_keypair error: 0x%02x\n", ret); goto cleanup; } /* * Now the keys have been created, generate an X.509 certificate * by populating the template and signing it. */ if ((ret = kmf_set_cert_pubkey(kmfhandle, &pubk, &certstruct))) { printf("kmf_set_cert_pubkey error: 0x%02x\n", ret); goto cleanup; } /* Version "2" is for an x509.v3 certificate */ if ((ret = kmf_set_cert_version(&certstruct, 2))) { printf("kmf_set_cert_version error: 0x%02x\n", ret); goto cleanup; } /* * Set up the serial number, it must be a KMF_BIGINT record. */ if ((ret = kmf_hexstr_to_bytes((uchar_t *)"0x010203", &serno.val, \ &serno.len))) { printf("kmf_hexstr_to_bytes error: 0x%02x\n", ret); goto cleanup; } if ((ret = kmf_set_cert_serial(&certstruct, &serno))) { printf("kmf_set_cert_serial error: 0x%02x\n", ret); goto cleanup; } if ((ret = kmf_set_cert_validity(&certstruct, NULL, ltime))) { printf("kmf_set_cert_validity error: 0x%02x\n", ret); goto cleanup; } if ((ret = kmf_set_cert_sig_alg(&certstruct, KMF_ALGID_SHA1WithRSA))) { printf("kmf_set_cert_sig_alg error: 0x%02x\n", ret); goto cleanup; } /* * Create a KMF_X509_NAME struct by parsing a distinguished name. */ if ((ret = kmf_dn_parser("cn=testcert", &certsubject))) { printf("kmf_dn_parser error: 0x%02x\n", ret); goto cleanup; } if ((ret = kmf_dn_parser("cn=testcert", &certissuer))) { printf("kmf_dn_parser error: 0x%02x\n", ret); goto cleanup; } if ((ret = kmf_set_cert_subject(&certstruct, &certsubject))) { printf("kmf_set_cert_sig_alg error: 0x%02x\n", ret); goto cleanup; } if ((ret = kmf_set_cert_issuer(&certstruct, &certissuer))) { printf("kmf_set_cert_sig_alg error: 0x%02x\n", ret); goto cleanup; } /* * Now we have the certstruct setup with the minimal amount needed * to generate a self-signed cert. Put together the attributes to * call kmf_sign_cert. */ numattrs = 0; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEY_HANDLE_ATTR, &prik, sizeof (KMF_KEY_HANDLE_ATTR)); numattrs++; /* The X509 template structure to be signed goes here. */ kmf_set_attr_at_index(attrlist, numattrs, KMF_X509_CERTIFICATE_ATTR, &certstruct, sizeof (KMF_X509_CERTIFICATE)); numattrs++; /* * Set the output buffer for the signed cert. * This will be a block of raw ASN.1 data. */ kmf_set_attr_at_index(attrlist, numattrs, KMF_CERT_DATA_ATTR, &rawcert, sizeof (KMF_DATA)); numattrs++; if ((ret = kmf_sign_cert(kmfhandle, numattrs, attrlist))) { printf("kmf_sign_cert error: 0x%02x\n", ret); goto cleanup; } /* * Now we have the certificate and we want to store it in the * keystore (which is the PKCS11 token in this example). */ numattrs = 0; kmf_set_attr_at_index(attrlist, numattrs, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); numattrs++; kmf_set_attr_at_index(attrlist, numattrs, KMF_CERT_DATA_ATTR, &rawcert, sizeof (KMF_DATA)); numattrs++; /* Use the same label as the public key */ kmf_set_attr_at_index(attrlist, numattrs, KMF_CERT_LABEL_ATTR, keylabel, strlen(keylabel)); numattrs++; if ((ret = kmf_store_cert(kmfhandle, numattrs, attrlist))) { printf("kmf_store_cert error: 0x%02x\n", ret); goto cleanup; } cleanup: kmf_free_data(&rawcert); kmf_free_dn(&certissuer); kmf_free_dn(&certsubject); kmf_finalize(kmfhandle); return (ret); }