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.
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.
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:
General purpose – C_Initialize(), C_Finalize(), C_GetInfo(), C_GetFunctionList()
Session management – C_OpenSession(), C_CloseSession(), C_GetSessionInfo(), C_CloseAllSessions(), C_Login(), C_Logout()
Slot and token management – C_GetSlotList(), C_GetSlotInfo(), C_GetMechanismList(), C_GetMechanismInfo(), C_SetPIN()
Encryption and decryption – C_EncryptInit(), C_Encrypt(), C_EncryptUpdate(), C_EncryptFinal(), C_DecryptInit(), C_Decrypt(), C_DecryptUpdate(), C_DecryptFinal()
Message digesting – C_DigestInit(), C_Digest(), C_DigestKey(), C_DigestUpdate(), C_DigestFinal()
Signing and applying MAC – C_Sign(), C_SignInit(), C_SignUpdate(), C_SignFinal(), C_SignRecoverInit(), C_SignRecover()
Signature verification – C_Verify(), C_VerifyInit(), C_VerifyUpdate(), C_VerifyFinal(), C_VerifyRecoverInit(), C_VerifyRecover()
Dual-purpose cryptographic functions – C_DigestEncryptUpdate(), C_DecryptDigestUpdate(), C_SignEncryptUpdate(), C_DecryptVerifyUpdate()
Random number generation – C_SeedRandom(), C_GenerateRandom()
Object management – C_CreateObject(), C_DestroyObject(), C_CopyObject(), C_FindObjects(), C_FindObjectsInit(), C_FindObjectsFinal(), C_GetAttributeValue(), C_SetAttributeValue()
Key management – C_GenerateKey(), C_GenerateKeyPair(), C_DeriveKey()
This section provides descriptions of the following functions for using PKCS #11:
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.
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.
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:
CKR_ARGUMENTS_BAD
CKR_CANT_LOCK
CKR_CRYPTOKI_ALREADY_INITIALIZED (not fatal)
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:
cryptokiVersion = 2, 11
manufacturerID = Sun Microsystems, Inc.
In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetInfo() gets the following return values:
CKR_ARGUMENTS_BAD
CKR_CRYPTOKI_NOT_INITIALIZED
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.
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:
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
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:
label – Sun Software PKCS#11 softtoken.
flags – CKF_DUAL_CRYPTO_OPERATIONS, CKF_TOKEN_INITIALIZED, CKF_RNG, CKF_USER_PIN_INITIALIZED, and CKF_LOGIN_REQUIRED, which are set to 1.
ulMaxSessionCount – Set to CK_EFFECTIVELY_INFINITE.
ulMaxRwSessionCount - Set to CK_EFFECTIVELY_INFINITE.
ulMaxPinLen – Set to 256.
ulMinPinLen – Set to 1.
ulTotalPublicMemory set to CK_UNAVAILABLE_INFORMATION.
ulFreePublicMemory set to CK_UNAVAILABLE_INFORMATION.
ulTotalPrivateMemory set to CK_UNAVAILABLE_INFORMATION.
ulFreePrivateMemory set to CK_UNAVAILABLE_INFORMATION.
In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetSlotlist() gets the following return values:
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
The following return values are relevant for plug-ins with hardware tokens:
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
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:
CKR_ARGUMENTS_BAD
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
CKR_TOKEN_WRITE_PROTECTED (occurs with write-protected tokens)
The following return values are relevant for plug-ins with hardware tokens:
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_SESSION_COUNT
CKR_SESSION_PARALLEL_NOT_SUPPORTED
CKR_SESSION_READ_WRITE_SO_EXISTS
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
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:
CKM_AES_CBC
CKM_AES_CBC_PAD
CKM_AES_ECB
CKM_AES_KEY_GEN
CKM_DES_CBC
CKM_DES_CBC_PAD
CKM_DES_ECB
CKM_DES_KEY_GEN
CKM_DES_MAC
CKM_DES_MAC_GENERAL
CKM_DES3_CBC
CKM_DES3_CBC_PAD
CKM_DES3_ECB
CKM_DES3_KEY_GEN
CKM_DH_PKCS_DERIVE
CKM_DH_PKCS_KEY_PAIR_GEN
CKM_DSA
CKM_DSA_KEY_PAIR_GEN
CKM_DSA_SHA_1
CKM_MD5
CKM_MD5_KEY_DERIVATION
CKM_MD5_RSA_PKCS
CKM_MD5_HMAC
CKM_MD5_HMAC_GENERAL
CKM_PBE_SHA1_RC4_128
CKM_PKCS5_PBKD2
CKM_RC4
CKM_RC4_KEY_GEN
CKM_RSA_PKCS
CKM_RSA_X_509
CKM_RSA_PKCS_KEY_PAIR_GEN
CKM_SHA_1
CKM_SHA_1_HMAC_GENERAL
CKM_SHA_1_HMAC
CKM_SHA_1_KEY_DERIVATION
CKM_SHA_1_RSA_PKCS
CKM_SSL3_KEY_AND_MAC_DERIVE
CKM_SSL3_MASTER_KEY_DERIVE
CKM_SSL3_MASTER_KEY_DERIVE_DH
CKM_SSL3_MD5_MAC
CKM_SSL3_PRE_MASTER_KEY_GEN
CKM_SSL3_SHA1_MAC
CKM_TLS_KEY_AND_MAC_DERIVE
CKM_TLS_MASTER_KEY_DERIVE
CKM_TLS_MASTER_KEY_DERIVE_DH
CKM_TLS_PRE_MASTER_KEY_GEN
In addition to CKR_FUNCTION_FAILED, CKR_GENERAL_ERROR, CKR_HOST_MEMORY, and CKR_OK, C_GetSlotlist() uses the following return values:
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
The following return values are relevant for plug-ins with hardware tokens:
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
In addition to the standard PKCS #11 functions, two convenience functions are supplied with the Oracle Solaris cryptographic framework:
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.
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.
This section includes the following examples:
This example uses PKCS #11 functions to create a digest from an input file. The example performs the following steps:
Specifies the digest mechanism.
In this example, the CKM_MD5 digest mechanism is used.
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.
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.
Conducts a digest operation with the slot.
The message digest is created in this task through these steps:
Opening the input file.
Initializing the digest operation by calling C_DigestInit().
Processing the data a piece at a time with C_DigestUpdate().
Ending the digest process by using C_DigestFinal() to get the complete digest.
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.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
#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); }
Example 9–2 creates a key object for encryption with the DES algorithm in the CBC mode. This source code performs the following steps:
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.
Defines a key object.
For this task, you have to set up a template for the key.
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.
Conducts an encryption operation in the slot.
The encryption is performed in this task through these steps:
Opening the input file.
Creating an object handle for the key.
Setting the encryption mechanism to CKM_DES_CBC_PAD by using the mechanism structure.
Initializing the encryption operation by calling C_EncryptInit().
Processing the data a piece at a time with C_EncryptUpdate().
Ending the encryption process by using C_EncryptFinal() to get the last portion of the encrypted data.
Conducts a decryption operation in the slot.
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.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
#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); }
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:
Defines a key object.
Sets the public key template.
Sets the private key template.
Creates a sample message.
Specifies the genmech mechanism, which generates the key pair.
Specifies the smech mechanism, which signs the key pair.
Initializes the cryptoki library.
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:
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.
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.
Opens the session by calling C_OpenSession().
Displays the public key with C_GetAttributeValue() – For demonstration purposes only.
Signing is started with C_SignInit() and completed with C_Sign().
Verification is started with C_VerifyInit() and completed with C_Verify().
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.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
#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); }
Example 9–4 demonstrates how to find a slot with a mechanism that can generate random bytes. The example performs the following steps:
Initializes the cryptoki library.
Calls GetRandSlot() to find a slot with a mechanism that can generate random bytes.
The task of finding a slot performs the following steps:
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.
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.
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.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
#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); }