Thepktool application is an excellent example ofhow to use the KMF APIs.
This section shows a simple application that uses KMF. This sectiondescribes the basic steps that an application needs to take in order to performsome KMF operations. This example assumes that you have experience in C programmingand a basic understanding of public key technologies and standards. This examplegoes through the steps of initializing KMF for use and then creates a self-signedX.509v3 certificate and associated RSA key pair. This example also shows howto use the KMF-enhancedpktool command to verify that theapplication was successful.
To give the program access to the KMF function prototypes and type definitions,include thekmfapi.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 thekmftypes.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 asKMF_KEYSTORE_PK11TOKEN
Key type, such asKMF_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 certificateand keypair by using thepktool command.
$pktool list objtype=bothEnter pin for Sun Software PKCS#11 softtoken :Found 1 certificates.1. (X.509 certificate) Label: admin@example.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=Example Park, O=Example Inc., OU=ExampleIT Office, CN=admin@example.com Issuer: C=US, ST=CA, L=Example Park, O=Example Inc., OU=Example ITOffice, CN=admin@example.com Serial: 0x452BF693 X509v3 Subject Alternative Name: email:admin@example.comFound 1 keys.Key #1 - RSA private key: admin@example.com
See thelibkmf(3LIB) manpage for definitionsof KMF APIs.
This application performs the following steps:
Before any KMF functions can be called, the application mustfirst usekmf_initialize() to initialize a handle for aKMF session. This handle is used as the first argument to most KMF functioncalls. It is an opaque data type and is used to hold internal state and contextinformation for that session.
This example application uses the PKCS#11 keystore. Usekmf_configure_keystore() to define a token to use for future operations.
The first step to create a certificate or a PKCS#10 CSR isto generate a keypair. Usekmf_create_keypair() to createboth the public and private keys needed and store the private key in the specifiedkeystore. The function returns handles to the application so that the callercan reference the public and private key objects in future operations if necessary.
Once a keypair is established, usekmf_set_cert_pubkey() andkmf_set_cert_version() to populate the template record that is usedto generate the final certificate. KMF provides different APIs for settingthe various fields of an X.509v3 certificate, including extensions. Usekmf_hexstr_to_bytes(),kmf_set_cert_serial(),kmf_set_cert_validity(), andkmf_set_cert_sig_alg() toset the serial number. The serial number is aKMF_BIGINT record.Usekmf_dn_parser(),kmf_set_cert_subject(),andkmf_set_cert_issuer() to create aKMF_X509_NAME structure.
Because this is a self-signed certificate creation exercise,this application signs the certificate template created above with the privatekey that goes with the public key in the certificate itself. Thiskmf_sign_cert() operation results in aKMF_DATA record thatcontains 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. Usekmf_store_cert() tostore the certificate in the PKCS#11 token defined at the beginning of thisapplication. The certificate could also be stored in NSS or an OpenSSL fileat this point.
Memory allocated to data structures generated by KMF shouldbe cleaned up when the data structure is no longer needed. KMF provides convenienceAPIs for properly deallocating memory associated with these objects. The propercleanup of memory is strongly encouraged in order to conserve resources. Cleanupinterfaces includekmf_free_data(),kmf_free_dn(),andkmf_finalize().
Below is the complete source code for this example application, includingall of the data types and helper functions. When you compile, be sure to includethe 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>intmain(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);}