本章介绍如何开发使用 PKCS #11 函数进行加密的用户级应用程序和提供者。其中包含以下主题:
有关加密框架的更多信息,请参阅第 8 章,Solaris 加密框架介绍。
Solaris 加密框架中的用户级应用程序通过 libpkcs11.so 模块中所提供的 cryptoki 库来访问 PKCS #11 函数。pkcs11_softtoken.so 模块是由 Sun Microsystems, Inc. 提供的 PKCS #11 软令牌实现,用于提供加密机制。软令牌插件是缺省的机制源。加密机制还可以通过第三方插件提供。
本节列出了软令牌所支持的 PKCS #11 函数和返回值,返回代码根据插入到框架中的提供者而异。本节还介绍了一些常见的函数。有关 cryptoki 库中所有元素的完整说明,请参阅相应的手册页或 http://www.rsasecurity.com/rsalabs/pkcs/pkcs-11。
下面列出了 Solaris 加密框架中的 pkcs11_softtoken.so 所支持的 PKCS #11 函数类别以及相关函数:
通用-C_Initialize()、C_Finalize()、C_GetInfo() 和 C_GetFunctionList()
会话管理-C_OpenSession()、C_CloseSession()、C_GetSessionInfo()、C_CloseAllSessions()、C_Login() 和 C_Logout()
插槽和令牌管理-C_GetSlotList()、C_GetSlotInfo()、C_GetMechanismList()、C_GetMechanismInfo() 和 C_SetPIN()
加密和解密-C_EncryptInit()、C_Encrypt()、C_EncryptUpdate()、C_EncryptFinal()、C_DecryptInit()、C_Decrypt()、C_DecryptUpdate() 和 C_DecryptFinal()
消息摘要-C_DigestInit()、C_Digest()、C_DigestKey()、C_DigestUpdate() 和 C_DigestFinal()
MAC 的签名和应用-C_Sign()、C_SignInit()、C_SignUpdate()、C_SignFinal()、C_SignRecoverInit() 和 C_SignRecover()
签名检验-C_Verify()、C_VerifyInit()、C_VerifyUpdate()、C_VerifyFinal()、C_VerifyRecoverInit() 和 C_VerifyRecover()
双重用途加密函数-C_DigestEncryptUpdate()、C_DecryptDigestUpdate()、C_SignEncryptUpdate() 和 C_DecryptVerifyUpdate()
随机数生成-C_SeedRandom() 和 C_GenerateRandom()
对象管理-C_CreateObject()、C_DestroyObject()、C_CopyObject()、C_FindObjects()、C_FindObjectsInit()、C_FindObjectsFinal()、C_GetAttributeValue() 和 C_SetAttributeValue()
密钥管理-C_GenerateKey()、C_GenerateKeyPair() 和 C_DeriveKey()
本节提供了以下使用 PKCS #11 的函数的说明:
所有的 PKCS #11 函数都可以从 libpkcs11.so 库中获取,不必使用 C_GetFunctionList() 函数来获取可用函数的列表。
C_Initialize() 可用于初始化 PKCS #11 库。C_Initialize() 使用以下语法:
C_Initialize(CK_VOID_PTR pInitArgs);
pInitArgs 是空值 NULL_PTR 或是指向 CK_C_INITIALIZE_ARGS 结构的指针。通过 NULL_PTR,该库可以将 Solaris 互斥锁用作锁定原语,在多个线程之间仲裁对内部共享结构的访问。请注意,Solaris 加密框架不接受互斥锁。由于 cryptoki 库的此实现可以安全高效地处理多线程,因此建议使用 NULL_PTR。应用程序还可以使用 pInitArgs 来设置诸如 CKF_LIBRARY_CANT_CREATE_OS_THREADS 之类的标志。C_Finalize() 表示应用程序使用 PKCS #11 库结束会话。
C_Finalize() 绝不应当通过库进行调用。按照惯例,应用程序负责调用 C_Finalize() 来关闭会话。
除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_Initialize() 还会使用以下返回值:
CKR_ARGUMENTS_BAD
CKR_CANT_LOCK
CKR_CRYPTOKI_ALREADY_INITIALIZED-此错误不是致命的。
C_GetInfo() 使用的是有关 cryptoki 库的制造商和版本信息。C_GetInfo() 使用以下语法:
C_GetInfo(CK_INFO_PTR pInfo);
C_GetInfo() 会返回以下值:
cryptokiVersion = 2, 11
manufacturerID = Sun Microsystems, Inc.
除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetInfo() 还可以获取以下返回值:
CKR_ARGUMENTS_BAD
CKR_CRYPTOKI_NOT_INITIALIZED
C_GetSlotList() 使用的是可用插槽的列表。如果除了 pkcs11_softtoken.so 以外尚未安装任何其他加密提供者,则 C_GetSlotList() 仅返回缺省插槽。C_GetSlotList() 使用以下语法:
C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount);
如果 tokenPresent 设置为 TRUE,则会将搜索限制在那些存在令牌的插槽。
如果 pSlotList 设置为 NULL_PTR,则 C_GetSlotlist() 仅返回插槽的数量。pulCount 是指向用于接收插槽计数的位置的指针。
如果 pSlotList 指向用于接收插槽的缓冲区,则 *pulCount 将设置为 CK_SLOT_ID 元素的最大预期数量。在返回时,*pulCount 将设置为 CK_SLOT_ID 元素的实际数量。
通常,PKCS #11 应用程序会调用 C_GetSlotList() 两次。第一次调用 C_GetSlotList() 用于获取进行内存分配的插槽数量,第二次调用 C_GetSlotList() 用于获取插槽。
除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetSlotlist() 还可以获取以下返回值:
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
C_GetTokenInfo() 可用于获取有关特定令牌的信息。C_GetTokenInfo() 使用以下语法:
C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo);
slotID 用于标识令牌的插槽。slotID 必须是由 C_GetSlotList() 返回的有效 ID。pInfo 是指向用于接收令牌信息的位置的指针。
如果 pkcs11_softtoken.so 是所安装的唯一提供者,则 C_GetTokenInfo() 将返回以下字段和值:
标记-Sun Software PKCS#11 软令牌。
标志-CKF_DUAL_CRYPTO_OPERATIONS、CKF_TOKEN_INITIALIZED、CKF_RNG、CKF_USER_PIN_INITIALIZED 和 CKF_LOGIN_REQUIRED,这些标志设置为 1。
ulMaxSessionCount-设置为 CK_EFFECTIVELY_INFINITE。
ulMaxRwSessionCount-设置为 CK_EFFECTIVELY_INFINITE。
ulMaxPinLen-设置为 256。
ulMinPinLen-设置为 1。
ulTotalPublicMemory-设置为 CK_UNAVAILABLE_INFORMATION。
ulFreePublicMemory-设置为 CK_UNAVAILABLE_INFORMATION。
ulTotalPrivateMemory-设置为 CK_UNAVAILABLE_INFORMATION。
ulFreePrivateMemory-设置为 CK_UNAVAILABLE_INFORMATION。
除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetSlotlist() 还可以获取以下返回值:
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
以下返回值与具有硬件令牌的插件相关:
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
应用程序可使用 C_OpenSession() 来启动特定插槽中具有特定令牌的加密会话。C_OpenSession() 使用以下语法:
C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession);
slotID 用于标识插槽。flags 用于指示会话是只读的还是可读写的。pApplication 是应用程序所定义的用于回调的指针。Notify 用于存放可选回调函数的地址。phSession 是指向会话句柄的位置的指针。
除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_OpenSession() 还可以获取以下返回值:
CKR_ARGUMENTS_BAD
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
CKR_TOKEN_WRITE_PROTECTED-随受写保护的令牌出现。
以下返回值与具有硬件令牌的插件相关:
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() 可用于获取指定令牌所支持的机制类型的列表。C_GetMechanismList() 使用以下语法:
C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount);
slotID 用于标识令牌的插槽。pulCount 是指向用于接收机制数量的位置的指针。 如果 pMechanismList 设置为 NULL_PTR,则 *pulCount 将返回机制的数量。否则,必须将 *pulCount 设置为列表的大小,pMechanismList 必须指向用于存放列表的缓冲区。
如果已插入 PKCS #11 软令牌,则 C_GetMechanismList() 将返回以下列出的支持的机制:
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
除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetSlotlist() 还使用以下返回值:
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
以下返回值与具有硬件令牌的插件相关:
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
除了标准的 PKCS #11 函数以外,Solaris 加密框架还附带了两个便利函数:
SUNW_C_GetMechSession() 是一个便利函数,用于初始化 Solaris 加密框架。该函数随后会使用指定的机制启动会话。SUNW_C_GetMechSession() 使用以下语法:
SUNW_C_GetMechSession(CK_MECHANISM_TYPE mech, C\ K_SESSION_HANDLE_PTR hSession)
mech 参数用于指定要使用的机制。hSession 是指向会话位置的指针。
SUNW_C_GetMechSession() 在内部调用 C_Initialize() 以初始化 cryptoki 库。SUNW_C_GetMechSession() 接着会使用指定的机制调用 C_GetSlotList() 和 C_GetMechanismInfo(),在可用插槽中搜索令牌。如果找到了机制,SUNW_C_GetMechSession() 会调用 C_OpenSession() 来打开会话。
SUNW_C_GetMechSession() 只需要调用一次。不过,多次调用 SUNW_C_GetMechSession() 也不会造成任何问题。
SUNW_C_KeyToObject() 可用于创建私钥对象。调用程序必须指定要使用的机制以及原始密钥数据。SUNW_C_KeyToObject() 可在内部确定指定机制的密钥类型。通用密钥对象是通过 C_CreateObject() 创建的。SUNW_C_KeyToObject() 接着会调用 C_GetSessionInfo() 和 C_GetMechanismInfo() 来获取插槽和机制。C_SetAttributeValue() 随后会根据机制的类型为密钥对象设置属性标志。
本节包含以下示例:
此示例使用 PKCS #11 函数通过输入文件创建摘要。该示例执行以下步骤:
指定摘要机制。
此示例中使用的是 CKM_MD5 摘要机制。
此示例使用 Sun 的便利函数 SUNW_C_GetMechSession()。SUNW_C_GetMechSession() 用于打开 cryptoki 库,该库用于存放 Solaris 加密框架中所使用的全部 PKCS #11 函数。SUNW_C_GetMechSession() 随后使用所需的机制来查找插槽。然后,将会启动会话。这个便利函数可有效地替换 C_Initialize() 调用、C_OpenSession() 调用以及查找支持指定机制的插槽所需的任何代码。
获取 cryptoki 信息。
本部分实际上不是创建消息摘要所必需的,之所以将其包括在内是为了说明 C_GetInfo() 函数的用法。此示例中将获取制造商 ID。其他信息选项用于检索版本和库数据。
针对插槽执行摘要操作。
此任务中的消息摘要可通过以下三个步骤创建:
打开输入文件。
通过调用 C_DigestInit() 来初始化摘要操作。
使用 C_DigestUpdate() 逐段处理数据。
使用 C_DigestFinal() 获取完整的摘要,从而结束摘要操作过程。
结束会话。
以下示例中显示了消息摘要示例的源代码。
此示例的源代码也可以通过 Sun 下载中心获取。请访问 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); }
示例 9–2 在 CBC(密码块链接)模式下使用 DES 算法为加密创建了密钥对象。此源代码执行以下步骤:
声明密钥材料。
定义 DES 和初始化向量。以静态方式声明的初始化向量仅用于说明,初始化向量应始终以动态方式定义并且永远不会重用。
定义密钥对象。
对于此任务,必须为密钥设置模板。
查找适用于指定加密机制的插槽。
此示例使用 Sun 的便利函数 SUNW_C_GetMechSession()。SUNW_C_GetMechSession() 用于打开 cryptoki 库,该库用于存放 Solaris 加密框架中所使用的全部 PKCS #11 函数。SUNW_C_GetMechSession() 随后使用所需的机制来查找插槽。然后,将会启动会话。这个便利函数可有效地替换 C_Initialize() 调用、C_OpenSession() 调用以及查找支持指定机制的插槽所需的任何代码。
在插槽中执行加密操作。
此任务中的加密可通过以下几个步骤执行:
在插槽中执行解密操作。
此任务中的解密可通过以下两个步骤执行。提供解密的目的仅是为了进行测试。
结束会话。
程序使用 C_CloseSession() 关闭会话,使用 C_Finalize() 关闭库。
以下示例中显示了对称加密示例的源代码。
此示例的源代码也可以通过 Sun 下载中心获取。请访问 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, ciphertext_space = BUFFERSIZ; CK_ULONG decrypttext_len; CK_ULONG total_encrypted = 0; CK_ULONG ulDatalen = BUFFERSIZ; CK_SLOT_ID SlotID; int *pi, i, bytes_read = 0; int error = 0; char inbuf[BUFFERSIZ]; FILE *fs; uchar_t *ciphertext, *pciphertext, *decrypttext; /* 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); }
本节中的示例生成了一个用于对简单字符串进行签名和检验的 RSA 密钥对。此示例执行以下步骤:
定义密钥对象。
设置公钥模板。
设置私钥模板。
创建样例消息。
指定用于生成密钥对象的 genmech 机制。
指定用于对密钥对进行签名的 smech 机制。
初始化 cryptoki 库。
通过用于生成和检验密钥对并对其进行签名的机制来查找插槽。此任务将使用一个名为 getMySlot() 的函数来执行以下步骤:
调用 C_GetSlotList() 函数以获取可用插槽的列表。
与 PKCS #11 约定中所建议的一样,C_GetSlotList() 需要调用两次。第一次调用 C_GetSlotList() 用于获取进行内存分配的插槽数量,第二次调用 C_GetSlotList() 用于检索插槽。
查找可以提供所需机制的插槽。
对于每个插槽,该函数都会调用 GetMechanismInfo() 以查找可用于生成密钥对并对其进行签名的机制。如果插槽不支持这些机制,则 GetMechanismInfo() 将返回错误。如果 GetMechanismInfo() 成功返回,则将检查机制标志,以确保这些机制可以执行所需的操作。
调用 C_OpenSession() 来打开会话。
关闭会话。
下面是签名和检验示例的源代码。
此示例的源代码也可以通过 Sun 下载中心获取。请访问 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); }
示例 9–4 说明了如何使用可以生成随机字节的机制来查找插槽。此示例执行以下步骤:
随机数生成样例的源代码如以下示例所示。
此示例的源代码也可以通过 Sun 下载中心获取。请访问 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); }
此示例称为演示提供者,是指可用作自定义提供者的基础的工作提供者。此示例可打开 PKCS#11 会话并创建对象。如果该提供者与包含 MD5Init()、MD5Update 和 MD5Final() 函数的库链接,则可通过使用 CKM_MD5 机制来执行摘要操作。该提供者基于 PKCS#11 V2.11。完整的提供者软件可以从 http://www.sun.com/download/products.xml?id=41912db5 下载。一些 PKCS#11 函数已在该示例代码中完全实现,而其他一些 CS#11 函数仅定义为返回 CKR_FUNCTION_NOT_SUPPORTED。
该示例由以下源代码和头文件组成:
exampleDigest.c -完全实现以下函数:C_DigestInit()、C_Digest()、C_DigestUpdate()、C_DigestKey() 和 C_DigestFinal()。
exampleDigestUtil.c -包含与摘要有关的实用程序函数。
exampleAttributeUtil.c -针对属性进行定义、分析、验证和执行各种操作。
exampleGeneral.c -完全实现以下函数:C_Initialize()、C_Finalize()、C_GetInfo()、C_GetFunctionList()、C_GetFunctionStatus() 和 C_CancelFunction()。
exampleObject.c -完全实现以下函数:C_CreateObject()、C_CopyObject()、C_DestroyObject()、C_GetAttributeValue() 和 C_SetAttributeValue()。
exampleObject.h -为密钥、属性和对象提供各种结构和函数原型。
exampleObjectUtil.c -定义用于管理密钥、对象和属性的特殊函数。
exampleOps.h -提供示例摘要函数的原型。
exampleRand.c -完全实现以下函数:C_SeedRandom() 和 C_GenerateRandom()。
exampleRandUtil.c -包含随机数实用程序函数。
exampleRand.h -提供 random_generator() 函数的原型。
exampleSession.c -完全实现以下函数:C_OpenSession()、C_CloseSession()、C_CloseAllSessions() 和 C_GetSessionInfo()。
exampleSessionUtil.c -包含与会话有关的实用程序函数。
exampleSession.h -提供会话所需的各种标志、结构、函数原型和宏。
exampleSlotToken.c -完全实现 C_GetSlotList()、C_GetSlotInfo()、C_GetTokenInfo()、C_GetMechanismList() 和 C_GetMechanismInfo()。
exampleKeys.c -包含以下可返回 CKR_FUNCTION_NOT_SUPPORTED 的函数:C_GenerateKey()、C_GenerateKeyPair()、C_WrapKey()、C_UnwrapKey() 和 C_DeriveKey()。
exampleEncrypt.c -包含以下可返回 CKR_FUNCTION_NOT_SUPPORTED 的函数:C_EncryptInit()、C_Encrypt()、C_EncryptUpdate() 和 C_EncryptFinal()。
exampleDecrypt.c -包含以下函数,这些函数可返回 CKR_FUNCTION_NOT_SUPPORTED:C_DecryptInit()、C_Decrypt()、C_DecryptUpdate() 和 C_DecryptFinal()。
exampleSign.c -包含以下函数,这些函数可返回 CKR_FUNCTION_NOT_SUPPORTED:C_SignInit()、C_Sign()、C_SignUpdate()、C_SignFinal()、C_SignRecoverInit() 和 C_SignRecover()。
exampleVerify.c -包含以下函数,这些函数可返回 CKR_FUNCTION_NOT_SUPPORTED:C_VerifyInit()、C_Verify()、C_VerifyUpdate()、C_VerifyFinal()、C_VerifyRecoverInit() 和 C_VerifyRecover()。
exampleDualCrypt.c -包含以下可返回 CKR_FUNCTION_NOT_SUPPORTED 的函数:C_DigestEncryptUpdate()、C_DecryptDigestUpdate()、C_SignEncryptUpdate() 和 C_DecryptVerifyUpdate()。
exampleGlobal.h -提供全局变量和常数的定义。