Solaris 开发者安全性指南

第 9 章 编写用户级加密应用程序和提供者

本章介绍如何开发使用 PKCS #11 函数进行加密的用户级应用程序和提供者。其中包含以下主题:

有关加密框架的更多信息,请参阅第 8 章,Solaris 加密框架介绍

Cryptoki 库概述

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

PKCS #11 函数列表

下面列出了 Solaris 加密框架中的 pkcs11_softtoken.so 所支持的 PKCS #11 函数类别以及相关函数:

使用 PKCS #11 的函数

本节提供了以下使用 PKCS #11 的函数的说明:


注 –

所有的 PKCS #11 函数都可以从 libpkcs11.so 库中获取,不必使用 C_GetFunctionList() 函数来获取可用函数的列表。


PKCS #11 函数: C_Initialize()

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() 还会使用以下返回值:

PKCS #11 函数: C_GetInfo()

C_GetInfo() 使用的是有关 cryptoki 库的制造商和版本信息。C_GetInfo() 使用以下语法:

C_GetInfo(CK_INFO_PTR pInfo);

C_GetInfo() 会返回以下值:

除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetInfo() 还可以获取以下返回值:

PKCS #11 函数: C_GetSlotList()

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() 还可以获取以下返回值:

PKCS #11 函数: C_GetTokenInfo()

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() 将返回以下字段和值:

除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetSlotlist() 还可以获取以下返回值:

以下返回值与具有硬件令牌的插件相关:

PKCS #11 函数: C_OpenSession()

应用程序可使用 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() 还可以获取以下返回值:

以下返回值与具有硬件令牌的插件相关:

PKCS #11 函数: C_GetMechanismList()

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() 将返回以下列出的支持的机制:

除了 CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY 和 CKR_OK 以外,C_GetSlotlist() 还使用以下返回值:

以下返回值与具有硬件令牌的插件相关:

扩展的 PKCS #11 函数

除了标准的 PKCS #11 函数以外,Solaris 加密框架还附带了两个便利函数:

扩展的 PKCS #11 函数: SUNW_C_GetMechSession()

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() 也不会造成任何问题。

扩展的 PKCS #11 函数: SUNW_C_KeyToObject

SUNW_C_KeyToObject() 可用于创建私钥对象。调用程序必须指定要使用的机制以及原始密钥数据。SUNW_C_KeyToObject() 可在内部确定指定机制的密钥类型。通用密钥对象是通过 C_CreateObject() 创建的。SUNW_C_KeyToObject() 接着会调用 C_GetSessionInfo()C_GetMechanismInfo() 来获取插槽和机制。C_SetAttributeValue() 随后会根据机制的类型为密钥对象设置属性标志。

用户级加密应用程序示例

本节包含以下示例:

消息摘要示例

    此示例使用 PKCS #11 函数通过输入文件创建摘要。该示例执行以下步骤:

  1. 指定摘要机制。

    此示例中使用的是 CKM_MD5 摘要机制。

  2. 查找适用于指定摘要算法的插槽。

    此示例使用 Sun 的便利函数 SUNW_C_GetMechSession()SUNW_C_GetMechSession() 用于打开 cryptoki 库,该库用于存放 Solaris 加密框架中所使用的全部 PKCS #11 函数。SUNW_C_GetMechSession() 随后使用所需的机制来查找插槽。然后,将会启动会话。这个便利函数可有效地替换 C_Initialize() 调用、C_OpenSession() 调用以及查找支持指定机制的插槽所需的任何代码。

  3. 获取 cryptoki 信息。

    本部分实际上不是创建消息摘要所必需的,之所以将其包括在内是为了说明 C_GetInfo() 函数的用法。此示例中将获取制造商 ID。其他信息选项用于检索版本和库数据。

  4. 针对插槽执行摘要操作。

    此任务中的消息摘要可通过以下三个步骤创建:

    1. 打开输入文件。

    2. 通过调用 C_DigestInit() 来初始化摘要操作。

    3. 使用 C_DigestUpdate() 逐段处理数据。

    4. 使用 C_DigestFinal() 获取完整的摘要,从而结束摘要操作过程。

  5. 结束会话。

    程序使用 C_CloseSession() 关闭会话,使用 C_Finalize() 关闭库。

以下示例中显示了消息摘要示例的源代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获取。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 9–1 使用 PKCS #11 函数创建消息摘要

#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 算法为加密创建了密钥对象。此源代码执行以下步骤:

  1. 声明密钥材料。

    定义 DES 和初始化向量。以静态方式声明的初始化向量仅用于说明,初始化向量应始终以动态方式定义并且永远不会重用。

  2. 定义密钥对象。

    对于此任务,必须为密钥设置模板。

  3. 查找适用于指定加密机制的插槽。

    此示例使用 Sun 的便利函数 SUNW_C_GetMechSession()SUNW_C_GetMechSession() 用于打开 cryptoki 库,该库用于存放 Solaris 加密框架中所使用的全部 PKCS #11 函数。SUNW_C_GetMechSession() 随后使用所需的机制来查找插槽。然后,将会启动会话。这个便利函数可有效地替换 C_Initialize() 调用、C_OpenSession() 调用以及查找支持指定机制的插槽所需的任何代码。

  4. 在插槽中执行加密操作。

    此任务中的加密可通过以下几个步骤执行:

    1. 调用 C_OpenSession() 来打开会话。

    2. 打开输入文件。

    3. 创建密钥的对象句柄。

    4. 使用机制结构将加密机制设置为 CKM_DES_CBC_PAD。

    5. 调用 C_EncryptInit() 来初始化加密操作。

    6. 使用 C_EncryptUpdate() 逐段处理数据。

    7. 使用 C_EncryptFinal() 获取最后一部分加密数据,从而结束加密过程。

  5. 在插槽中执行解密操作。

    此任务中的解密可通过以下两个步骤执行。提供解密的目的仅是为了进行测试。

    1. 调用 C_DecryptInit() 来初始化解密操作。

    2. 使用 C_Decrypt() 处理整个字符串。

  6. 结束会话。

    程序使用 C_CloseSession() 关闭会话,使用 C_Finalize() 关闭库。

以下示例中显示了对称加密示例的源代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获取。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 9–2 使用 PKCS #11 函数创建加密密钥对象

#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 密钥对。此示例执行以下步骤:

  1. 定义密钥对象。

  2. 设置公钥模板。

  3. 设置私钥模板。

  4. 创建样例消息。

  5. 指定用于生成密钥对象的 genmech 机制。

  6. 指定用于对密钥对进行签名的 smech 机制。

  7. 初始化 cryptoki 库。

  8. 通过用于生成和检验密钥对并对其进行签名的机制来查找插槽。此任务将使用一个名为 getMySlot() 的函数来执行以下步骤:

    1. 调用 C_GetSlotList() 函数以获取可用插槽的列表。

      与 PKCS #11 约定中所建议的一样,C_GetSlotList() 需要调用两次。第一次调用 C_GetSlotList() 用于获取进行内存分配的插槽数量,第二次调用 C_GetSlotList() 用于检索插槽。

    2. 查找可以提供所需机制的插槽。

      对于每个插槽,该函数都会调用 GetMechanismInfo() 以查找可用于生成密钥对并对其进行签名的机制。如果插槽不支持这些机制,则 GetMechanismInfo() 将返回错误。如果 GetMechanismInfo() 成功返回,则将检查机制标志,以确保这些机制可以执行所需的操作。

  9. 调用 C_OpenSession() 来打开会话。

  10. 使用 C_GenerateKeyPair() 来生成密钥对。

  11. 使用 C_GetAttributeValue() 显示公钥-仅用于说明。

  12. 签名以 C_SignInit() 开始,以 C_Sign() 结束。

  13. 检验以 C_VerifyInit() 开始,以 C_Verify() 结束。

  14. 关闭会话。

    程序使用 C_CloseSession() 关闭会话,使用 C_Finalize() 关闭库。

下面是签名和检验示例的源代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获取。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 9–3 使用 PKCS #11 函数对文本进行签名和检验

#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 说明了如何使用可以生成随机字节的机制来查找插槽。此示例执行以下步骤:

  1. 初始化 cryptoki 库。

  2. 调用 GetRandSlot(),以便使用可以生成随机字节的机制来查找插槽。

      插槽查找任务执行以下步骤:

    1. 调用 C_GetSlotList() 函数以获取可用插槽的列表。

      与 PKCS #11 约定中所建议的一样,C_GetSlotList() 需要调用两次。第一次调用 C_GetSlotList() 用于获取进行内存分配的插槽数量,第二次调用 C_GetSlotList() 用于检索插槽。

    2. 查找可以生成随机字节的插槽。

      对于每个插槽,该函数都可以使用 GetTokenInfo() 来获取令牌信息,并在设置了 CKF_RNG 标志的情况下检查匹配项。如果找到设置了 CKF_RNG 标志的插槽,则 GetRandSlot() 函数将返回。

  3. 使用 C_OpenSession() 来打开会话。

  4. 使用 C_GenerateRandom() 来生成随机字节。

  5. 结束会话。

    程序使用 C_CloseSession() 关闭会话,使用 C_Finalize() 关闭库。

随机数生成样例的源代码如以下示例所示。


注 –

此示例的源代码也可以通过 Sun 下载中心获取。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 9–4 使用 PKCS #11 函数生成随机数

#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()MD5UpdateMD5Final() 函数的库链接,则可通过使用 CKM_MD5 机制来执行摘要操作。该提供者基于 PKCS#11 V2.11。完整的提供者软件可以从 http://www.sun.com/download/products.xml?id=41912db5 下载。一些 PKCS#11 函数已在该示例代码中完全实现,而其他一些 CS#11 函数仅定义为返回 CKR_FUNCTION_NOT_SUPPORTED。

该示例由以下源代码和头文件组成: