Developer's Guide to Oracle Solaris Security

Chapter 9 Writing User–Level Cryptographic Applications and Providers

This chapter explains how to develop user–level applications and providers that use the PKCS #11 functions for cryptography.

The following topics are covered:

For more information on the cryptographic framework, refer to Chapter 8, Introduction to the Oracle Solaris Cryptographic Framework.

Overview of the Cryptoki Library

User-level applications in the Oracle Solaris cryptographic framework access PKCS #11 functions through the cryptoki library, which is provided in the libpkcs11.so module. The pkcs11_softtoken.so module is a PKCS #11 Soft Token implementation that is provided by Oracle Corporation to supply cryptographic mechanisms. The soft token plug-in is the default source of mechanisms. Cryptographic mechanisms can also be supplied through third-party plug-ins.

This section lists the PKCS #11 functions and return values that are supported by the soft token. Return codes vary depending on the providers that are plugged into the framework. The section also describes some common functions. For a complete description of all the elements in the cryptoki library, refer to libpkcs11(3LIB) or to PKCS #11: Cryptographic Token Interface Standard on the RSA Laboratories web site.

Ensure that direct bindings are used for all providers. See ld(1) and the Linker and Libraries Guide for more information.

PKCS #11 Function List

The following list shows the categories of PKCS #11 functions that are supported by pkcs11_softtoken.so in the Oracle Solaris cryptographic framework with the associated functions:

Functions for Using PKCS #11

This section provides descriptions of the following functions for using PKCS #11:


Note –

All the PKCS #11 functions are available from libpkcs11.so library. You do not have to use the C_GetFunctionList() function to get the list of functions available.


PKCS #11 Functions: C_Initialize()

C_Initialize() initializes the PKCS #11 library. C_Initialize() uses the following syntax:

C_Initialize(CK_VOID_PTR pInitArgs);

pInitArgs is either the null value NULL_PTR or else a pointer to a CK_C_INITIALIZE_ARGS structure. With NULL_PTR, the library uses the Oracle Solaris mutexes as locking primitives to arbitrate the access to internal shared structures between multiple threads. Note that the Oracle Solaris cryptographic framework does not accept mutexes. Because this implementation of the cryptoki library handles multithreading safely and efficiently, using NULL_PTR is recommended. An application can also use pInitArgs to set flags such as CKF_LIBRARY_CANT_CREATE_OS_THREADS. C_Finalize() signals that the application is through with the PKCS #11 library.


Note –

C_Finalize() should never be called by libraries. By convention, applications are responsible for calling C_Finalize() to close out a session.


In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_Initialize() uses the following return values:

PKCS #11 Functions: C_GetInfo()

C_GetInfo() uses manufacturer and version information about the cryptoki library. C_GetInfo() uses the following syntax:

C_GetInfo(CK_INFO_PTR pInfo);

C_GetInfo() returns the following values:

In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetInfo() gets the following return values:

PKCS #11 Functions: C_GetSlotList()

C_GetSlotList() uses a list of available slots. If no additional cryptographic providers have been installed other than pkcs11_softtoken.so, then C_GetSlotList() returns the default slot only. C_GetSlotList() uses the following syntax:

C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, 
CK_ULONG_PTR pulCount);

When set to TRUE, tokenPresent limits the search to those slots whose tokens are present.

When pSlotList is set to NULL_PTR, C_GetSlotlist() returns the number of slots only. pulCount is a pointer to the location to receive the slot count.

When pSlotList points to the buffer to receive the slots, *pulCount is set to the maximum expected number of CK_SLOT_ID elements. On return, *pulCount is set to the actual number of CK_SLOT_ID elements.

Typically, PKCS #11 applications call C_GetSlotList() twice. The first time, C_GetSlotList() is called to get the number of slots for memory allocation. The second time, C_GetSlotList() is called to retrieve the slots.


Note –

The order of the slots is not guaranteed. The order of the slots can vary with each load of the PKCS #11 library.


In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetSlotlist() gets the following return values:

PKCS #11 Functions: C_GetTokenInfo()

C_GetTokenInfo() gets information about a specific token. C_GetTokenInfo() uses the following syntax:

C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo);

slotID identifies the slot for the token. slotID has to be a valid ID that was returned by C_GetSlotList(). pInfo is a pointer to the location to receive the token information.

If pkcs11_softtoken.so is the only installed provider, then C_GetTokenInfo() returns the following fields and values:

In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetSlotlist() gets the following return values:

The following return values are relevant for plug-ins with hardware tokens:

PKCS #11 Functions: C_OpenSession()

C_OpenSession() enables an application to start a cryptographic session with a specific token in a specific slot. C_OpenSession() uses the following syntax:

C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, 
CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession);

slotID identifies the slot. flags indicates whether the session is read-write or read-only. pApplication is a pointer that is defined by the application for use in callbacks. Notify holds the address of an optional callback function. phSession is a pointer to the location of the session handle.

In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_OpenSession() gets the following return values:

The following return values are relevant for plug-ins with hardware tokens:

PKCS #11 Functions: C_GetMechanismList()

C_GetMechanismList() gets a list of mechanism types that are supported by the specified token. C_GetMechanismList() uses the following syntax:

C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, 
CK_ULONG_PTR pulCount);

slotID identifies the slot for the token. pulCount is a pointer to the location to receive the number of mechanisms. When pMechanismList is set to NULL_PTR, the number of mechanisms is returned in *pulCount. Otherwise, *pulCount must be set to the size of the list and pMechanismList points to the buffer to hold the list.

When PKCS #11 Soft Token is plugged in, C_GetMechanismList() returns the following list of supported mechanisms:

In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetSlotlist() uses the following return values:

The following return values are relevant for plug-ins with hardware tokens:

Extended PKCS #11 Functions

In addition to the standard PKCS #11 functions, two convenience functions are supplied with the Oracle Solaris cryptographic framework:

Extended PKCS #11 Functions: SUNW_C_GetMechSession()

SUNW_C_GetMechSession() is a convenience function that initializes the Oracle Solaris cryptographic framework. The function then starts a session with the specified mechanism. SUNW_C_GetMechSession() uses the following syntax:

SUNW_C_GetMechSession(CK_MECHANISM_TYPE mech, C\
K_SESSION_HANDLE_PTR hSession)

The mech parameter is used to specify the mechanism to be used. hSession is a pointer to the session location.

Internally, SUNW_C_GetMechSession() calls C_Initialize() to initialize the cryptoki library. SUNW_C_GetMechSession() next calls C_GetSlotList() and C_GetMechanismInfo() to search through the available slots for a token with the specified mechanism. When the mechanism is found, SUNW_C_GetMechSession() calls C_OpenSession() to open a session.

The SUNW_C_GetMechSession() only needs to be called once. However, calling SUNW_C_GetMechSession() multiple times does not cause any problems.

Extended PKCS #11 Functions: SUNW_C_KeyToObject

SUNW_C_KeyToObject() creates a secret key object. The calling program must specify the mechanism to be used and raw key data. Internally, SUNW_C_KeyToObject() determines the type of key for the specified mechanism. A generic key object is created through C_CreateObject(). SUNW_C_KeyToObject() next calls C_GetSessionInfo() and C_GetMechanismInfo() to get the slot and mechanism. C_SetAttributeValue() then sets the attribute flag for the key object according to the type of mechanism.

User-Level Cryptographic Application Examples

This section includes the following examples:

Message Digest Example

    This example uses PKCS #11 functions to create a digest from an input file. The example performs the following steps:

  1. Specifies the digest mechanism.

    In this example, the CKM_MD5 digest mechanism is used.

  2. Finds a slot that is capable of the specified digest algorithm.

    This example uses the Sun convenience function SUNW_C_GetMechSession(). SUNW_C_GetMechSession() opens the cryptoki library, which holds all the PKCS #11 functions that are used in the Oracle Solaris cryptographic framework. SUNW_C_GetMechSession() then finds the slot with the desired mechanism. The session is then started. Effectively, this convenience function replaces the C_Initialize() call, the C_OpenSession() call, and any code needed to find a slot that supports the specified mechanism.

  3. Obtains cryptoki information.

    This part is not actually needed to create the message digest, but is included to demonstrate use of the C_GetInfo() function. This example gets the manufacturer ID. The other information options retrieve version and library data.

  4. Conducts a digest operation with the slot.

      The message digest is created in this task through these steps:

    1. Opening the input file.

    2. Initializing the digest operation by calling C_DigestInit().

    3. Processing the data a piece at a time with C_DigestUpdate().

    4. Ending the digest process by using C_DigestFinal() to get the complete digest.

  5. Ends the session.

    The program uses C_CloseSession() to close the session and C_Finalize() to close the library.

The source code for the message digest example is shown in the following example.


Note –

The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.



Example 9–1 Creating a Message Digest Using PKCS #11 Functions

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define BUFFERSIZ    8192
#define MAXDIGEST    64

/* Calculate the digest of a user supplied file. */
void
main(int argc, char **argv)
{
	CK_BYTE digest[MAXDIGEST];
	CK_INFO info;
	CK_MECHANISM mechanism;
	CK_SESSION_HANDLE hSession;
	CK_SESSION_INFO Info;
	CK_ULONG ulDatalen = BUFFERSIZ;
	CK_ULONG ulDigestLen = MAXDIGEST;
	CK_RV rv;
	CK_SLOT_ID SlotID;

	int i, bytes_read = 0;
	char inbuf[BUFFERSIZ];
	FILE *fs;
	int error = 0;

	/* Specify the CKM_MD5 digest mechanism as the target */
	mechanism.mechanism = CKM_MD5;
	mechanism.pParameter = NULL_PTR;
	mechanism.ulParameterLen = 0;

	/* Use SUNW convenience function to initialize the cryptoki
	 * library, and open a session with a slot that supports
	 * the mechanism we plan on using. */
	rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
	if (rv != CKR_OK) {
		fprintf(stderr, "SUNW_C_GetMechSession: rv = 0x%.8X\n", rv);
		exit(1);
	}

	/* Get cryptoki information, the manufacturer ID */
	rv = C_GetInfo(&info);
	if (rv != CKR_OK) {
		fprintf(stderr, "WARNING: C_GetInfo: rv = 0x%.8X\n", rv);
	}
	fprintf(stdout, "Manufacturer ID = %s\n", info.manufacturerID);

	/* Open the input file */
	if ((fs = fopen(argv[1], "r")) == NULL) {
		perror("fopen");
		fprintf(stderr, "\n\tusage: %s filename>\n", argv[0]);
		error = 1;
		goto exit_session;
	}

	/* Initialize the digest session */
	if ((rv = C_DigestInit(hSession, &mechanism)) != CKR_OK) {
		fprintf(stderr, "C_DigestInit: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_digest;
	}

	/* Read in the data and create digest of this portion */
	while (!feof(fs) && (ulDatalen = fread(inbuf, 1, BUFFERSIZ, fs)) > 0) {
		if ((rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)inbuf,
				    ulDatalen)) != CKR_OK) {
			fprintf(stderr, "C_DigestUpdate: rv = 0x%.8X\n", rv);
			error = 1;
			goto exit_digest;
		}
		bytes_read += ulDatalen;
	}
	fprintf(stdout, "%d bytes read and digested!!!\n\n", bytes_read);

	/* Get complete digest */
	ulDigestLen = sizeof (digest);
	if ((rv = C_DigestFinal(hSession, (CK_BYTE_PTR)digest,
			    &ulDigestLen)) != CKR_OK) {
		fprintf(stderr, "C_DigestFinal: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_digest;
	}

	/* Print the results */
	fprintf(stdout, "The value of the digest is: ");
	for (i = 0; i < ulDigestLen; i++) {
		fprintf(stdout, "%.2x", digest[i]);
	}
	fprintf(stdout, "\nDone!!!\n");

exit_digest:
	fclose(fs);

exit_session:
	(void) C_CloseSession(hSession);

exit_program:
	(void) C_Finalize(NULL_PTR);

	exit(error);

}

Symmetric Encryption Example

    Example 9–2 creates a key object for encryption with the DES algorithm in the CBC mode. This source code performs the following steps:

  1. Declares key materials.

    Defines DES and initialization vector. The initialization vector is declared statically for demonstration purposes only. Initialization vectors should always be defined dynamically and never reused.

  2. Defines a key object.

    For this task, you have to set up a template for the key.

  3. Finds a slot that is capable of the specified encryption mechanism.

    This example uses the Sun convenience function SUNW_C_GetMechSession(). SUNW_C_GetMechSession() opens the cryptoki library, which holds all the PKCS #11 functions that are used in the Oracle Solaris cryptographic framework. SUNW_C_GetMechSession() then finds the slot with the desired mechanism. The session is then started. Effectively, this convenience function replaces the C_Initialize() call, the C_OpenSession() call, and any code needed to find a slot that supports the specified mechanism.

  4. Conducts an encryption operation in the slot.

      The encryption is performed in this task through these steps:

    1. Opening the input file.

    2. Creating an object handle for the key.

    3. Setting the encryption mechanism to CKM_DES_CBC_PAD by using the mechanism structure.

    4. Initializing the encryption operation by calling C_EncryptInit().

    5. Processing the data a piece at a time with C_EncryptUpdate().

    6. Ending the encryption process by using C_EncryptFinal() to get the last portion of the encrypted data.

  5. Conducts a decryption operation in the slot.

      The decryption is performed in this task through these steps. The decryption is provided for testing purposes only.

    1. Initializes the decryption operation by calling C_DecryptInit().

    2. Processes the entire string with C_Decrypt().

  6. Ends the session.

    The program uses C_CloseSession() to close the session and C_Finalize() to close the library.

The source code for the symmetric encryption example is shown in the following example.


Note –

The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.



Example 9–2 Creating an Encryption Key Object Using PKCS #11 Functions

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define BUFFERSIZ    8192

/* Declare values for the key materials. DO NOT declare initialization 
 * vectors statically like this in real life!! */
uchar_t des_key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
uchar_t des_cbc_iv[] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef};

/* Key template related definitions. */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_OBJECT_CLASS class = CKO_SECRET_KEY;
static CK_KEY_TYPE keyType = CKK_DES;


/* Example encrypts and decrypts a file provided by the user. */
void
main(int argc, char **argv)
{
	CK_RV rv;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE hKey;
	CK_SESSION_HANDLE hSession;
	CK_ULONG ciphertext_len = 64, lastpart_len = 64;
	long ciphertext_space = BUFFERSIZ;
	CK_ULONG decrypttext_len;
	CK_ULONG total_encrypted = 0;
	CK_ULONG ulDatalen = BUFFERSIZ;

	int i, bytes_read = 0;
	int error = 0;
	char inbuf[BUFFERSIZ];
	FILE *fs;
	uchar_t ciphertext[BUFFERSIZ], *pciphertext, decrypttext[BUFFERSIZ];

	/* Set the key object */
	CK_ATTRIBUTE template[] = {
		{CKA_CLASS, &class, sizeof (class) },
		{CKA_KEY_TYPE, &keyType, sizeof (keyType) },
		{CKA_TOKEN, &falsevalue, sizeof (falsevalue) },
		{CKA_ENCRYPT, &truevalue, sizeof (truevalue) },
		{CKA_VALUE, &des_key, sizeof (des_key) }
	};

	/* Set the encryption mechanism to CKM_DES_CBC_PAD */
	mechanism.mechanism = CKM_DES_CBC_PAD;
	mechanism.pParameter = des_cbc_iv;
	mechanism.ulParameterLen = 8;

	/* Use SUNW convenience function to initialize the cryptoki
	 * library, and open a session with a slot that supports
	 * the mechanism we plan on using. */
	rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);

	if (rv != CKR_OK) {
		fprintf(stderr, "SUNW_C_GetMechSession: rv = 0x%.8X\n", rv);
		exit(1);
	}

	/* Open the input file */
	if ((fs = fopen(argv[1], "r")) == NULL) {
		perror("fopen");
		fprintf(stderr, "\n\tusage: %s filename>\n", argv[0]);
		error = 1;
		goto exit_session;
	}

	/* Create an object handle for the key */
	rv = C_CreateObject(hSession, template,
	    sizeof (template) / sizeof (CK_ATTRIBUTE),
	    &hKey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_CreateObject: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}


	/* Initialize the encryption operation in the session */
	rv = C_EncryptInit(hSession, &mechanism, hKey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_EncryptInit: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	/* Read in the data and encrypt this portion */
	pciphertext = &ciphertext[0];
	while (!feof(fs) && (ciphertext_space > 0) &&
	    (ulDatalen = fread(inbuf, 1, ciphertext_space, fs)) > 0) {
		ciphertext_len = ciphertext_space;

		/* C_EncryptUpdate is only being sent one byte at a
		 * time, so we are not checking for CKR_BUFFER_TOO_SMALL.
		 * Also, we are checking to make sure we do not go
		 * over the alloted buffer size.  A more robust program
		 * could incorporate realloc to enlarge the buffer
		 * dynamically.	 */
		rv = C_EncryptUpdate(hSession, (CK_BYTE_PTR)inbuf, ulDatalen,
		    pciphertext, &ciphertext_len);
		if (rv != CKR_OK) {
			fprintf(stderr, "C_EncryptUpdate: rv = 0x%.8X\n", rv);
			error = 1;
			goto exit_encrypt;
		}
		pciphertext += ciphertext_len;
		total_encrypted += ciphertext_len;
		ciphertext_space -= ciphertext_len;
		bytes_read += ulDatalen;
	}

	if (!feof(fs) || (ciphertext_space < 0)) {
		fprintf(stderr, "Insufficient space for encrypting the file\n");
		error = 1;
		goto exit_encrypt;
	}

	/* Get the last portion of the encrypted data */
	lastpart_len = ciphertext_space;
	rv = C_EncryptFinal(hSession, pciphertext, &lastpart_len);
	if (rv != CKR_OK) {
		fprintf(stderr, "C_EncryptFinal: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_encrypt;
	}
	total_encrypted += lastpart_len;

	fprintf(stdout, "%d bytes read and encrypted. Size of the "
	    "ciphertext: %d!\n\n", bytes_read, total_encrypted);

	/* Print the encryption results */
	fprintf(stdout, "The value of the encryption is:\n");
	for (i = 0; i < ciphertext_len; i++) {
		if (ciphertext[i] < 16)
			fprintf(stdout, "0%x", ciphertext[i]);
		else
			fprintf(stdout, "%2x", ciphertext[i]);
	}

	/* Initialize the decryption operation in the session */
	rv = C_DecryptInit(hSession, &mechanism, hKey);

	/* Decrypt the entire ciphertext string */
	decrypttext_len = sizeof (decrypttext);
	rv = C_Decrypt(hSession, (CK_BYTE_PTR)ciphertext, total_encrypted,
	    decrypttext, &decrypttext_len);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Decrypt: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_encrypt;
	}

	fprintf(stdout, "\n\n%d bytes decrypted!!!\n\n", decrypttext_len);

	/* Print the decryption results */
	fprintf(stdout, "The value of the decryption is:\n%s", decrypttext);

	fprintf(stdout, "\nDone!!!\n");

exit_encrypt:
	fclose(fs);

exit_session:
	(void) C_CloseSession(hSession);

exit_program:
	(void) C_Finalize(NULL_PTR);
	exit(error);
}

Sign and Verify Example

    The example in this section generates an RSA key pair. The key pair is used to sign and verify a simple string. The example goes through the following steps:

  1. Defines a key object.

  2. Sets the public key template.

  3. Sets the private key template.

  4. Creates a sample message.

  5. Specifies the genmech mechanism, which generates the key pair.

  6. Specifies the smech mechanism, which signs the key pair.

  7. Initializes the cryptoki library.

  8. Finds a slot with mechanisms for signing, verifying, and key pair generation.

      The task uses a function that is called getMySlot(), which performs the following steps:

    1. Calling the function C_GetSlotList() to get a list of the available slots.

      C_GetSlotList() is called twice, as the PKCS #11 convention suggests. C_GetSlotList() is called the first time to get the number of slots for memory allocation. C_GetSlotList() is called the second time to retrieve the slots.

    2. Finding a slot that can supply the desired mechanisms.

      For each slot, the function calls GetMechanismInfo() to find mechanisms for signing and for key pair generation. If the mechanisms are not supported by the slot, GetMechanismInfo() returns an error. If GetMechanismInfo() returns successfully, then the mechanism flags are checked to make sure the mechanisms can perform the needed operations.

  9. Opens the session by calling C_OpenSession().

  10. Generates the key pair by using C_GenerateKeyPair().

  11. Displays the public key with C_GetAttributeValue() – For demonstration purposes only.

  12. Signing is started with C_SignInit() and completed with C_Sign().

  13. Verification is started with C_VerifyInit() and completed with C_Verify().

  14. Closes the session.

    The program uses C_CloseSession() to close the session and C_Finalize() to close the library.

The source code for the sign-and-verify example follows.


Note –

The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.



Example 9–3 Signing and Verifying Text Using PKCS #11 Functions

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define BUFFERSIZ	8192

/* Define key template */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_ULONG modulusbits = 1024;
static CK_BYTE public_exponent[] = {3};

boolean_t GetMySlot(CK_MECHANISM_TYPE sv_mech, CK_MECHANISM_TYPE kpgen_mech,
    CK_SLOT_ID_PTR pslot);

/* Example signs and verifies a simple string, using a public/private
 * key pair. */
void
main(int argc, char **argv)
{
	CK_RV   rv;
	CK_MECHANISM genmech, smech;
	CK_SESSION_HANDLE hSession;
	CK_SESSION_INFO sessInfo;
	CK_SLOT_ID slotID;
	int error, i = 0;

	CK_OBJECT_HANDLE privatekey, publickey;

    /* Set public key. */
	CK_ATTRIBUTE publickey_template[] = {
		{CKA_VERIFY, &truevalue, sizeof (truevalue)},
		{CKA_MODULUS_BITS, &modulusbits, sizeof (modulusbits)},
		{CKA_PUBLIC_EXPONENT, &public_exponent,
		    sizeof (public_exponent)}
	};

    /* Set private key. */
	CK_ATTRIBUTE privatekey_template[] = {
		{CKA_SIGN, &truevalue, sizeof (truevalue)},
		{CKA_TOKEN, &falsevalue, sizeof (falsevalue)},
		{CKA_SENSITIVE, &truevalue, sizeof (truevalue)},
		{CKA_EXTRACTABLE, &truevalue, sizeof (truevalue)}
	};

    /* Create sample message. */
	CK_ATTRIBUTE getattributes[] = {
		{CKA_MODULUS_BITS, NULL_PTR, 0},
		{CKA_MODULUS, NULL_PTR, 0},
		{CKA_PUBLIC_EXPONENT, NULL_PTR, 0}
	};

	CK_ULONG messagelen, slen, template_size;

	boolean_t found_slot = B_FALSE;
	uchar_t *message = (uchar_t *)"Simple message for signing & verifying.";
	uchar_t *modulus, *pub_exponent;
	char sign[BUFFERSIZ];
	slen = BUFFERSIZ;

	messagelen = strlen((char *)message);

	/* Set up mechanism for generating key pair */
	genmech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
	genmech.pParameter = NULL_PTR;
	genmech.ulParameterLen = 0;

	/* Set up the signing mechanism */
	smech.mechanism = CKM_RSA_PKCS;
	smech.pParameter = NULL_PTR;
	smech.ulParameterLen = 0;

	/* Initialize the CRYPTOKI library */
	rv = C_Initialize(NULL_PTR);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Initialize: Error = 0x%.8X\n", rv);
		exit(1);
	}

	found_slot = GetMySlot(smech.mechanism, genmech.mechanism, &slotID);

	if (!found_slot) {
		fprintf(stderr, "No usable slot was found.\n");
		goto exit_program;
	}

	fprintf(stdout, "selected slot: %d\n", slotID);

	/* Open a session on the slot found */
	rv = C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
	    &hSession);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_OpenSession: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_program;
	}

	fprintf(stdout, "Generating keypair....\n");

	/* Generate Key pair for signing/verifying */
	rv = C_GenerateKeyPair(hSession, &genmech, publickey_template,
	    (sizeof (publickey_template) / sizeof (CK_ATTRIBUTE)),
	    privatekey_template,
	    (sizeof (privatekey_template) / sizeof (CK_ATTRIBUTE)),
	    &publickey, &privatekey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_GenerateKeyPair: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	/* Display the publickey. */
	template_size = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);

	rv = C_GetAttributeValue(hSession, publickey, getattributes,
	    template_size);

	if (rv != CKR_OK) {
		/* not fatal, we can still sign/verify if this failed */
		fprintf(stderr, "C_GetAttributeValue: rv = 0x%.8X\n", rv);
		error = 1;
	} else {
		/* Allocate memory to hold the data we want */
		for (i = 0; i < template_size; i++) {
			getattributes[i].pValue = 
			    malloc (getattributes[i].ulValueLen * 
				sizeof(CK_VOID_PTR));
			if (getattributes[i].pValue == NULL) {
				int j;
				for (j = 0; j < i; j++)
					free(getattributes[j].pValue);
				goto sign_cont;
			}
		}

		/* Call again to get actual attributes */
		rv = C_GetAttributeValue(hSession, publickey, getattributes,
		    template_size);

		if (rv != CKR_OK) {
			/* not fatal, we can still sign/verify if failed */
			fprintf(stderr,
			    "C_GetAttributeValue: rv = 0x%.8X\n", rv);
			error = 1;
		} else {
			/* Display public key values */
			fprintf(stdout, "Public Key data:\n\tModulus bits: "
			    "%d\n", 
			    *((CK_ULONG_PTR)(getattributes[0].pValue)));

			fprintf(stdout, "\tModulus: ");
			modulus = (uchar_t *)getattributes[1].pValue;
			for (i = 0; i < getattributes[1].ulValueLen; i++) {
				fprintf(stdout, "%.2x", modulus[i]);
			}

			fprintf(stdout, "\n\tPublic Exponent: ");
			pub_exponent = (uchar_t *)getattributes[2].pValue;
			for (i = 0; i< getattributes[2].ulValueLen; i++) {
				fprintf(stdout, "%.2x", pub_exponent[i]);
			}
			fprintf(stdout, "\n");
		}
	}
	
sign_cont:	
	rv = C_SignInit(hSession, &smech, privatekey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_SignInit: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	rv = C_Sign(hSession, (CK_BYTE_PTR)message, messagelen,
	    (CK_BYTE_PTR)sign, &slen);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Sign: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	fprintf(stdout, "Message was successfully signed with private key!\n");

	rv = C_VerifyInit(hSession, &smech, publickey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_VerifyInit: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	rv = C_Verify(hSession, (CK_BYTE_PTR)message, messagelen,
	    (CK_BYTE_PTR)sign, slen);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Verify: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	fprintf(stdout, "Message was successfully verified with public key!\n");

exit_session:
	(void) C_CloseSession(hSession);

exit_program:
	(void) C_Finalize(NULL_PTR);

	for (i = 0; i < template_size; i++) {
		if (getattributes[i].pValue != NULL)
			free(getattributes[i].pValue);
	}

	exit(error);

}

/* Find a slot capable of:
 * . signing and verifying with sv_mech
 * . generating a key pair with kpgen_mech
 * Returns B_TRUE when successful. */
boolean_t GetMySlot(CK_MECHANISM_TYPE sv_mech, CK_MECHANISM_TYPE kpgen_mech,
    CK_SLOT_ID_PTR pSlotID)
{
	CK_SLOT_ID_PTR pSlotList = NULL_PTR;
	CK_SLOT_ID SlotID;
	CK_ULONG ulSlotCount = 0;
	CK_MECHANISM_INFO mech_info;
	int i;
	boolean_t returnval = B_FALSE;

	CK_RV rv;

	/* Get slot list for memory alloction */
	rv = C_GetSlotList(0, NULL_PTR, &ulSlotCount);

	if ((rv == CKR_OK) && (ulSlotCount > 0)) {
		fprintf(stdout, "slotCount = %d\n", ulSlotCount);
		pSlotList = malloc(ulSlotCount * sizeof (CK_SLOT_ID));

		if (pSlotList == NULL) {
			fprintf(stderr, "System error: unable to allocate "
			    "memory\n");
			return (returnval);
		}

		/* Get the slot list for processing */
		rv = C_GetSlotList(0, pSlotList, &ulSlotCount);
		if (rv != CKR_OK) {
			fprintf(stderr, "GetSlotList failed: unable to get "
			    "slot count.\n");
			goto cleanup;
		}
	} else {
		fprintf(stderr, "GetSlotList failed: unable to get slot "
		    "list.\n");
		return (returnval);
	}

	/* Find a slot capable of specified mechanism */
	for (i = 0; i < ulSlotCount; i++) {
		SlotID = pSlotList[i];

		/* Check if this slot is capable of signing and
		 * verifying with sv_mech. */
		rv = C_GetMechanismInfo(SlotID, sv_mech, &mech_info);

		if (rv != CKR_OK) {
			continue;
		}

		if (!(mech_info.flags & CKF_SIGN &&
			mech_info.flags & CKF_VERIFY)) {
			continue;
		}

		/* Check if the slot is capable of key pair generation
		 * with kpgen_mech. */
		rv = C_GetMechanismInfo(SlotID, kpgen_mech, &mech_info);

		if (rv != CKR_OK) {
			continue;
		}

		if (!(mech_info.flags & CKF_GENERATE_KEY_PAIR)) {
			continue;
		}

		/* If we get this far, this slot supports our mechanisms. */
		returnval = B_TRUE;
		*pSlotID = SlotID;
		break;

	}

cleanup:
	if (pSlotList)
		free(pSlotList);

	return (returnval);

}

Random Byte Generation Example

    Example 9–4 demonstrates how to find a slot with a mechanism that can generate random bytes. The example performs the following steps:

  1. Initializes the cryptoki library.

  2. Calls GetRandSlot() to find a slot with a mechanism that can generate random bytes.

      The task of finding a slot performs the following steps:

    1. Calling the function C_GetSlotList() to get a list of the available slots.

      C_GetSlotList() is called twice, as the PKCS #11 convention suggests. C_GetSlotList() is called the first time to get the number of slots for memory allocation. C_GetSlotList() is called the second time to retrieve the slots.

    2. Finding a slot that can generate random bytes.

      For each slot, the function obtains the token information by using GetTokenInfo() and checks for a match with the CKF_RNG flag set. When a slot that has the CKF_RNG flag set is found, the GetRandSlot() function returns.

  3. Opens the session by using C_OpenSession().

  4. Generates random bytes by using C_GenerateRandom().

  5. Ends the session.

    The program uses C_CloseSession() to close the session and C_Finalize() to close the library.

The source code for the random number generation sample is shown in the following example.


Note –

The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.



Example 9–4 Generating Random Numbers Using PKCS #11 Functions

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define RANDSIZE 64

boolean_t GetRandSlot(CK_SLOT_ID_PTR pslot);

/* Example generates random bytes. */
void
main(int argc, char **argv)
{
	CK_RV   rv;
	CK_MECHANISM mech;
	CK_SESSION_HANDLE hSession;
	CK_SESSION_INFO sessInfo;
	CK_SLOT_ID slotID;
	CK_BYTE randBytes[RANDSIZE];

	boolean_t found_slot = B_FALSE;
	int error;
	int i;

	/* Initialize the CRYPTOKI library */
	rv = C_Initialize(NULL_PTR);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Initialize: Error = 0x%.8X\n", rv);
		exit(1);
	}

	found_slot = GetRandSlot(&slotID);

	if (!found_slot) {
		goto exit_program;
	}

	/* Open a session on the slot found */
	rv = C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
	    &hSession);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_OpenSession: rv = 0x%.8x\n", rv);
		error = 1;
		goto exit_program;
	}

	/* Generate random bytes */
	rv = C_GenerateRandom(hSession, randBytes, RANDSIZE);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_GenerateRandom: rv = 0x%.8x\n", rv);
		error = 1;
		goto exit_session;
	}

	fprintf(stdout, "Random value: ");
	for (i = 0; i < RANDSIZE; i++) {
		fprintf(stdout, "%.2x", randBytes[i]);
	}

exit_session:
	(void) C_CloseSession(hSession);

exit_program:
	(void) C_Finalize(NULL_PTR);
	exit(error);

}

boolean_t
GetRandSlot(CK_SLOT_ID_PTR pslot)
{
	CK_SLOT_ID_PTR pSlotList;
	CK_SLOT_ID SlotID;
	CK_TOKEN_INFO tokenInfo;
	CK_ULONG ulSlotCount;
	CK_MECHANISM_TYPE_PTR pMechTypeList = NULL_PTR;
	CK_ULONG ulMechTypecount;
	boolean_t result = B_FALSE;
	int i = 0;

	CK_RV rv;

	/* Get slot list for memory allocation */
	rv = C_GetSlotList(0, NULL_PTR, &ulSlotCount);

	if ((rv == CKR_OK) && (ulSlotCount > 0)) {
		fprintf(stdout, "slotCount = %d\n", (int)ulSlotCount);
		pSlotList = malloc(ulSlotCount * sizeof (CK_SLOT_ID));

		if (pSlotList == NULL) {
			fprintf(stderr,
			    "System error: unable to allocate memory\n");
			return (result);
		}

		/* Get the slot list for processing */
		rv = C_GetSlotList(0, pSlotList, &ulSlotCount);
		if (rv != CKR_OK) {
			fprintf(stderr, "GetSlotList failed: unable to get "
			    "slot list.\n");
			free(pSlotList);
			return (result);
		}
	} else {
		fprintf(stderr, "GetSlotList failed: unable to get slot"
		    " count.\n");
		return (result);
	}

	/* Find a slot capable of doing random number generation */
	for (i = 0; i < ulSlotCount; i++) {
		SlotID = pSlotList[i];

		rv = C_GetTokenInfo(SlotID, &tokenInfo);

		if (rv != CKR_OK) {
			/* Check the next slot */
			continue;
		}

		if (tokenInfo.flags & CKF_RNG) {
			/* Found a random number generator */
			*pslot = SlotID;
			fprintf(stdout, "Slot # %d supports random number "
			    "generation!\n", SlotID);
			result = B_TRUE;
			break;
		}
	}

	if (pSlotList)
		free(pSlotList);

	return (result);

}