下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

最新资讯
简化任何设备的身份验证云连接。
利用 CoAP 设计节能型云连接 IoT 解决方案。
11.0.0 版 FreeRTOS 内核简介:
FreeRTOS 路线图和代码贡献流程。
使用 FreeRTOS 实现 OPC-UA over TSN。

corePKCS11 管理和随机数演示

 

简介

本演示是 corePKCS11 演示系列中的第一个。它涵盖了 PKCS #11 API 的管理 PKCS #11堆栈章节,并介绍了如何使用 PKCS #11 生成随机数。PKCS #11 标准可在 此处获取。

这些演示及其相关网页使用 PKCS #11 定义的术语可与 PKCS #11 API、堆栈、标准等术语替换使用。对标准中定义的术语稍作总结:"Cryptoki" 是指定义在 PKCS #11 标准中的加密 令牌接口,即 PKCS #11 API 或函数。Cryptoki 的实现 是指“Cryptoki 库”,等同于“PKCS #11 实现”或 “PKCS #11 堆栈”。

PKCS #11 规范分为三个头文件:

pkcs11.h
这是主头文件,是应用程序唯一需要包含的文件。它需要在被包含之前 定义一些宏,这些定义位于 "core_pkcs11.h" 文件中(请参阅 <freertos>/libraries/freertos_plus/source/corepkcs11/include/core_pkcs11.h)。因此,包含 PKCS #11 库时,要先包含 "core_pkcs11.h",然后再包含 "pkcs11.h"。
pkcs11f.h
这包含了函数原型。
pkcs11t.h
这包含了 PKCS #11 规范中定义的各种类型。

PKCS #11 演示项目使用 FreeRTOSWindows 移植,因此可以在 Windows 上使用 Visual Studio 免费社区版进行构建和评估,无需 任何特定的 MCU 硬件。

此演示中展示的函数集可分为:

    • 通用函数
    • 插槽和令牌管理函数
    • 会话管理函数
    • 随机数生成函数

 

源代码组织

基于 PKCS #11 的双向验证演示的 Visual Studio 解决方案名为 pkcs11_demo.sln,位于 FreeRTOS 主下载文件的 FreeRTOS\FreeRTOS-Plus\Demo\corePKCS11_Windows_Simulator\ 目录中。

点击放大

 

配置演示项目

要配置演示项目,请将 configPKCS11_MANAGEMENT_AND_RNG_DEMO (位于 pkcs11_demo_config.h 中)设置为 1。

 

构建演示项目

演示项目使用 Visual Studio 的免费社区版本

  1. 在Visual Studio IDE 中,打开 Visual Studio 解决方案文件 FreeRTOS\FreeRTOS-Plus\Demo\corePKCS11_Windows_Simulator\pkcs11_demos.sln
  2. 在 IDE 的 build 菜单中选择 build solution
 

功能

此演示的入口点是 vPKCS11ManagementAndRNGDemo。此函数大致描述了如何启动 PKCS #11 会话并用它生成一个随机数的基本流程。

第一步是获取包含指向PKCS #11 堆栈所实现函数的函数指针。 结构体。未实现的函数始终为 NULL。这一点很有用,因为一些 PKCS #11 实现可能并不定义每个函数。演示将断言未实现的函数。考虑到 这种情况,它在真正的应用程序中会很有用,使得库在不同的 PKCS #11 实现之间兼容。

获取 PKCS #11 函数列表:


/* The CK_FUNCTION_LIST is a structure that contains the Cryptoki version

* and a function pointer to each function in the Cryptoki API. If the

* function pointer is NULL it is unimplemented.

*/

CK_FUNCTION_LIST_PTR pxFunctionList = NULL;

/* We use the function list returned by C_GetFunctionList to see what functions

* the Cryptoki library supports. We use asserts to ensure that all the

* functionality needed in this demo is available.

*/

xResult = C_GetFunctionList( &pxFunctionList );
configASSERT( xResult == CKR_OK );
configASSERT( pxFunctionList != NULL );
configASSERT( pxFunctionList->C_Initialize != NULL );
configASSERT( pxFunctionList->C_GetSlotList != NULL );
configASSERT( pxFunctionList->C_OpenSession != NULL );
configASSERT( pxFunctionList->C_Login != NULL );
configASSERT( pxFunctionList->C_GenerateRandom != NULL );
configASSERT( pxFunctionList->C_CloseSession != NULL );
configASSERT( pxFunctionList->C_Finalize != NULL );

从 PKCS #11 实现中获取函数后,PKCS #11 实现可以通过 调用 C_Initialize 进行初始化。此函数不保证线程安全,在进行此调用时 应谨慎考虑并发问题。演示使用的 PKCS #11 堆栈未实现规范中的 线程安全机制。因此,在使用不同的 PKCS #11 堆栈时,可能需要重新审视调用 C_Initialize 的 任何代码,以确保正确处理并发。

初始化 PKCS #11 栈:


/* This Cryptoki library does not implement any initialization arguments. At the time of

* writing this demo, the purpose of these optional arguments is to provide

* function pointers for mutex operations.

*/

CK_C_INITIALIZE_ARGS xInitArgs = { 0 };

/* C_Initialize will initialize the Cryptoki library and the hardware it

* abstracts.

*/

xResult = pxFunctionList->C_Initialize( &xInitArgs );
configASSERT( xResult == CKR_OK );

初始化后,我们会查询应用程序可以使用的插槽。插槽由 PKCS #11 定义为 "可能包含令牌的逻辑读取器"。演示将始终使用第一个 由 C_GetSlotList 返回的插槽,因为它要写入的实现只有一个插槽。 该插槽是应用程序可以指定要使用的令牌。许多实现在插槽到令牌的关系上 有所不同。要点是 SlotID 将指定我们要用于应用程序会话的 令牌。

选择一个插槽:


/* A slot ID is an integer that defines a slot. The Cryptoki definition of

* a slot is "A logical reader that potentially contains a token."

*

* Essentially it is an abstraction for accessing the token. The reason for

* this is some tokens are a physical "card' that needs to be inserted into

* a slot for the device to read.

*

* A concrete example of a slot could be a USB Hardware Security Module (HSM),

* which generally appears as a singular slot, and abstracts it's internal "token".

*

* Some implementations have multiple slots mapped to a single token, or maps

* a slot per token.

*/

CK_SLOT_ID * pxSlotId = NULL;

/* CK_ULONG is a long unsigned integer as defined by PKCS #11. */
CK_ULONG xSlotCount = 0;

/* C_GetSlotList will retrieve an array of CK_SLOT_IDs.

* This Cryptoki library does not implement slots, but it is important to

* highlight how Cryptoki can be used to interface with real hardware.

*

* By setting the first argument "tokenPresent" to true, we only retrieve

* slots that have a token. If the second argument "pSlotList" is NULL, the

* third argument "pulCount" will be modified to contain the total slots.

*/

xResult = pxFunctionList->C_GetSlotList( CK_TRUE,
NULL,
&xSlotCount );
configASSERT( xResult == CKR_OK );

/* Since C_GetSlotList does not allocate the memory itself for getting a list

* of CK_SLOT_ID, we allocate one for it to populate with the list of

* slot ids.

*/

pxSlotId = pvPortMalloc( sizeof( CK_SLOT_ID ) * ( xSlotCount ) );
configASSERT( pxSlotId != NULL );

/* Now since pSlotList is not NULL, C_GetSlotList will populate it with the

* available slots.

*/

xResult = pxFunctionList->C_GetSlotList( CK_TRUE,
pxSlotId,
&xSlotCount );
configASSERT( xResult == CKR_OK );

下一步是创建一个会话,将应用程序连接到由 PKCS #11 堆栈返回的插槽。规范 将会话定义为“应用程序与令牌之间的逻辑连接”。同样, 源代码和 PKCS #11 规范中会进一步对此进行解释。会话通过调用 C_OpenSession 建立,并指定应用程序要使用的插槽。

开启 PKCS #11 会话:


/* A session is defined to be "The logical connection between an application

* and a token."

*

* The session can either be private or public, and differentiates

* your application from the other users of the token.

*/

CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;

/* Since this Cryptoki library does not actually implement the concept of slots,

* but we will use the first available slot, so the demo code conforms to

* Cryptoki.

*

* C_OpenSession will establish a session between the application and

* the token and we can then use the returned CK_SESSION_HANDLE for

* cryptographic operations with the token.

*

* For legacy reasons, Cryptoki demands that the CKF_SERIAL_SESSION bit

* is always set.

*/

xResult = pxFunctionList->C_OpenSession( pxSlotId[0],
CKF_SERIAL_SESSION | CKF_RW_SESSION,
NULL, /* Application defined pointer. */
NULL, /* Callback function. */
&hSession );
configASSERT( xResult == CKR_OK );

既然会话已经建立,我们可以使用它来生成一个随机数。在加密运算中经常使用 随机数,加密中使用的许多算法都依赖于随机数生成器的“随机性”, 以确保密码不会被破解。

生成随机数缓冲区:


/* CK_BYTE is a PKCS #11 type that is defined as an unsigned char. */
CK_BYTE xRandomData[ 10 ] = { 0 };

/* C_GenerateRandom generates random or pseudo random data. As arguments it

* takes the application session, and a pointer to a byte buffer, as well as

* the length of the byte buffer. Then it will fill this buffer with random

* bytes.

*/

xResult = pxFunctionList->C_GenerateRandom( hSession,
xRandomData,
sizeof( xRandomData ) );
configASSERT( xResult == CKR_OK );

for( ulIndex = 0; ulIndex < sizeof( xRandomData ); ulIndex++ )
{
configPRINTF( ( "Generated random number: %x\r\n", xRandomData[ ulIndex ] ) );
}

由于加密操作已完成,演示使用以下步骤清理资源:

  1. 关闭活动会话,
  2. 调用 C_Finalize 以取消初始化 PKCS #11 堆栈。

清理:


/* C_CloseSession closes the session that was established between the

* application and the token. This will clean up the resources that maintained

* the link between the application and the token. If the application wishes

* to use the token again, it will need to open a new session.

*/

xResult = pxFunctionList->C_CloseSession( hSession );
configASSERT( xResult == CKR_OK );

/* C_Finalize signals to the Cryptoki library that the application is done

* using it. It should always be the last call to the Cryptoki library.

* NULL should always be passed as the argument, as the parameter is currently

* just reserved for future revisions.

*

* Calling this function in a multi threaded environment can lead to undefined

* behavior if other threads are accessing the Cryptoki library.

*/

xResult = pxFunctionList->C_Finalize( NULL );
configASSERT( xResult == CKR_OK );
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.