内容


Linux 上的 PKCS #11 openCryptoki

API 允许在密码硬件和用户空间应用程序之间建立接口

openCryptoki 是什么?

openCryptoki 是 PKCS #11 API 的一种实现,它允许建立到保存了密码信息并执行密码功能的设备(譬如智能卡、智能磁盘或者 PCMCIA 卡)的接口。openCryptoki 通过将应用程序从密码设备细节的分离来提供应用程序的可移植性。应用程序的隔离还提供了添加的安全性层,因为所有的密码信息驻留在设备里。openCryptoki API 提供了应用程序和各种可移植密码设备之间的标准编程接口。

体系结构概述

openCryptoki 由槽(slot)管理器以及槽令牌动态链接库(STDLL)的 API 组成。槽管理器作为守护进程来运行以控制向应用程序提供的令牌槽数,并且使用一个共享内存区域与应用程序交互。每个设备有一个相关联的令牌并将令牌插入到槽管理器数据库中的一个槽。共享内存区域允许在应用程序之间进行状态信息的适当共享以帮助确保与 PKCS #11 规范保持一致。

槽管理器

槽管理器守护进程( pkcsslotd )管理系统中的槽(因此也包括令牌)。固定数量的进程可以被附加到 pkcsslotd ,因此将使用共享内存中的一个静态表。当前对表的限制是最多 1000 个进程可以使用子系统。守护进程在初始化时设置将这个共享内存并在其后扮演垃圾收集器的角色以确保只有活动进程保持注册。当一个进程附加到一个槽并打开一个会话, pkcsslotd 将告知后来的进程已有一个会话打开并将锁定特定的函数调用,如果它们需要独占地访问给定令牌的话。守护进程不断地搜索它在共享内存中的区域以确定附加到令牌的进程真的在运行。如果一个附加的进程异常终止, pkcsslotd 将在那个进程之后“打扫干净”并释放槽供其它进程使用。

主 API

STDLL 的主 API 位于 /usr/lib/pkcs11/PKCS11_API.so 中。这个 API 包含 PKCS #11 API 规范中所描述的所有函数。主 API 为每个应用程序提供槽管理设施。这个 API 还装入特定于令牌的模块(STDLL)来提供特定于令牌的操作(密码操作、会话管理和对象管理)。STDLL 是为每个令牌类型定制的并具有特定函数,譬如初始化例程以允许令牌与槽管理器一起工作。当应用程序用 C_Initialize 调用初始化子系统时,API 将为所有存在于配置中(驻留在共享内存)的令牌装入 STDLL 共享对象并且调用特定于令牌的初始化例程。

槽令牌动态链接库(STDLL)

STDLL 是主 API 的插件模块。它们提供超出主 API 函数之外的特定于令牌的函数。可以通过为设备构建 STDLL 来支持特定设备。每个 STDLL 必须至少提供一个特定于令牌的初始化函数。如果该设备是智能设备,譬如支持多种机制的硬件适配器,STDLL 可以变得简单因为设备能存储大多数会话信息。如果设备只执行简单的密码功能,则所有对象必须由软件来管理。这种灵活性使得 STDLL 可以支持任何密码设备。

共享内存

槽管理器在共享内存区域设置其数据库。因为允许附加到 pkcsslotd 的进程的最大数目是有限的,所以可以为令牌管理留出固定数量的内存。这块为令牌管理分配的固定内存使得应用程序更容易地访问令牌状态信息并确保和 PKCS #11 规范的一致性。

openCryptoki 的组件

本节描述了 openCryptoki 子系统的不同组件。

槽管理器守护进程

槽管理器守护程序是一个可执行文件( /usr/sbin/pkcsslotd ),它读取 /etc/pkcs11/pk_config_data ,依照系统中找到的设备安排共享内存。然后 pkcsslotd 继续作为守护进程运行。任何其它试图使用子系统的应用程序必须首先附加到共享内存区域并作为 API 初始化进程的一部分注册,所以 pkcsslotd 知道应用程序。如果 /etc/pkcs11/pk_config_data 被更改了,必须停止 pkcsslotd 并重新启动以读取新的配置文件。守护进程可以通过发出 kill <pkcsslotd config> 命令来停止。如果有任何应用程序正在使用子系统,守护进程将不会终止。

PKCS11_API.so

这个库包含主 API( /usr/lib/pkcs11/PKCS11_API.so )并由使用由子系统所管理的任何 PKCS #11 令牌的任何应用程序装入。在应用程序使用令牌之前,它必须装入 API 并调用 C_Initialize ,以符合 PKCS #11 规范。装入操作是通过使用 dlopen 设施的应用程序执行的。

槽令牌 DLL(STDLL)

在初始的销售产品中装有三个 STDLL。它们使用两种不同的固件装入(firmware load)来支持 IBM 4758 PCI Cryptographic Coprocessor,以及 IBM Cryptographic Accelerator(ICA),2058 型或 Leeds Lite 卡。

注:编译进程试图构建目标平台所支持的所有令牌,以及所有需要的支持程序。如果未提供一部分头和库,这些组件将不被构建。

IBM 4758 PCI Cryptographic Coprocessor 支持
IBM 4758 PCI Cryptographic Coprocessor(4758 Coprocessor)是“联邦信息处理标准 4(Federal Information Processing Standards 4(FIPS4))”干扰响应包中的一个可编程密码协处理器。作为一个可编程设备,它可以被赋予多重“人格”。openCryptoki 提供 STDLL 来建立到两种不同的固件装入的接口:deep 和 shallow。

deep 固件装入提供了运行在卡上的完整的 PKCS #11 实现。在这种情况下 STDLL 把主机 PKCS #11 操作转化为传递到卡的命令块。在这种模式下,4758 Coprocessor 的完整安全特性是可用的。API 模块所装入的共享对象是 /usr/lib/pkcs11/stdll/PKCS11_4758.so ;但是没有应用程序需要知道令牌名称因为配置进程为适当的令牌检测和配置子系统。这个装入可从 4758 Coprocessor 网站获得(参阅 参考资料)。

shallow 装入没有使用卡的任何安全特性。在这种情况下,STDLL 要复杂的多,提供 PKCS #11 对象和会话管理以及 PKCS #11 规范要求的允许多进程或线程的互操作。API 所装入的模块是 /usr/lib/pkcs11/stdll/PKCS11_LW.so 。这个固件在来自 IBM 的受限基础上可用,并在本质上将 4758 Coprocessor 变成了密码处理器。

IBM Cryptographic Accelerator 2058 型支持
ICA 设备是照 4758 Coprocessor 的 shallow 固件模式建模的。它提供所有的对象和会话管理设施,并将 PKCS #11 机制暴露给应用程序。API 为应用程序装入 /usr/lib/pkcs11/stdll/PKCS11_ICA.so 模块。这个模块唯一的特性就是它利用设备驱动程序提供的透明负载均衡。子系统仅在设备上打开一个,但是驱动程序将负载分布到系统中每个 ICA 设备上。系统中有多个设备时,有一个容错级别。只要系统中还有一个设备在起作用,应用程序将继续起作用但是性能降低。

openCryptoki 入门

本节描述了 openCryptoki 的系统需求。它还说明了您可以到哪儿获取 openCryptoki 以及如何编译和安装它。

系统需求

运行 openCryptoki 的系统必须至少支持一种 openCryptoki 所提供的令牌(4758 Cryptographic Coprocessor、ICA 2058 型和 System/390 Linux Cryptographic 硬件支持)。

以下列表显示了运行 openCryptoki 的系统需求。

硬件需求:

  • x86 PCI 总线系统,或
  • 具有 Linux 密码硬件支持的 System/390

软件需求:

  • 至少运行 2.2.16 内核的 Linux 操作系统
  • 安装令牌的设备驱动程序以及相关的支持库(还可能需要来自那些“分发版(distribution)”的一些头文件)

获取 openCryptoki

openCryptoki 项目位于 IBM developerWorks 网站,在这里可以得到源代码。从这个网站还可以得到提供错误修正和功能增强的更新。

编译与安装 openCryptoki

假设系统硬件符合所需设备的设备支持(以及头文件),您可以通过在顶层目录运行 make 来构建 openCryptoki。使用 build 目标仅允许代码编译。install 目标试图在当前系统安装函数。设置 INSROOT 环境变量将允许您安装到不同位置。clean 目标清除目标文件以及来自源树的其它构建输出。

如果您只为一个特定设备构建 openCryptoki,那么源树的顶级 Makefile 应该被修改。每个组件有它自己的 Makefile,并且顶级 Makefile 遍历它所有的子目录构建它所找到的所有 Makefile。要构建特定组件,不必使用顶级 Makefile。在任意给定的子目录运行 make -f 命令就构建特定组件。用 make -f ../Makefile.linux 命令从那个目录构建具有 Linux 目录的组件。

配置 openCryptoki

本节说明了与配置 openCryptoki 有关的各种组件和过程。

配置实用程序

一共有三个配置实用程序,它们是 openCryptoki 工具箱的一个部分。它们是:

/usr/lib/pkcs11/methods/pkcs11_startup
这个实用程序是一个可执行的 shell 脚本,用来构建配置信息( /etc/pkcs11/pkcs11_config_data )。
/usr/lib/pkcs11/methods/4758_status 这个实用程序是一个二进制可执行文件,用来查询 4758 Coprocessor 以确定卡上是 shallow 还是 deep 固件装入。
/usr/lib/pkcs11/methods/pkcs_slot
这个实用程序是一个 shell 脚本,用来将配置文件写到磁盘的 /etc/pkcs11/config_data

要简化启动,建议您创建一个称之为 /etc/rc.pkcs11 的 shell 脚本。这个脚本的示例在 Preparing the system for configuration章节中。

配置文件与数据存储

一些 STDLL 需要本地磁盘空间来存储持久数据,譬如令牌信息、个人识别号码(PIN)以及令牌对象。对于 shallow 4758 令牌,这个信息存储在 /etc/pkcs11/4758shallow ,对于 ICA 设备它存储在 /etc/pkcs11/lite 。每个目录中有一个子目录 TOK_OBJ ,它包含令牌对象。每个 STDLL 被限制到 2048 个公用的和 2048 个私有的令牌对象或者分配给 /etc 的磁盘空间。配置脚本自动创建所需目录并且分配所需权限。

注:FindObject 函数的性能与系统中存在的对象的数量直接相关。应该实现应用程序,以使它们不做这么多不必要的搜索。

/etc/pkcs11/pk_config_data 文件存储了系统中当前令牌的所有配置信息。当运行时,子系统的共享内存包含了实际有用的配置信息。

为配置准备系统

为了确保对 openCryptoki 令牌的正确访问,只有 root 用户和是“pkcs11”组成员的用户被允许访问子系统。要正确配置系统,完成下列步骤:

  1. 运行
    groupadd pkcs11命令来创建“pkcs11”组。
    注: Red Hat Linux 系统支持该命令。参考您的 Linux 分发版文档以查找在您的实现不包含 groupadd 命令时如何添加组。
  2. 通过将用户标识加到
    /etc/group 文件或者通过使用分发版的管理 GUI,把要使用子系统的任何用户标识添加到 pkcs11 组。或者,组所拥有的并且设置组标识符为 pkcs11 的应用程序也将能运行。配置检测脚本将创建数据存储位置并分配正确的所有权。
  3. 创建一个 shell 脚本以在系统引导时启动子系统。
  4. 将下列脚本添加到本地系统启动脚本。例如,在一个运行 Red Hat Linux 的系统上,把它添加为
    /etc/rc.pkcs11 并在 /etc/rc.local 上添加一个对它的调用。
清单 1
#!/bin/bash
# Sample /etc/rc.pkcs11
# script for starting pkcs#11 at system IPL.
# This should be added to the end of the rc.local script, or
# whatever local startup script for your distribution applies
# Generate the configuration information
/usr/lib/pkcs11/methods/pkcs11_startup
# start the subsystem
/usr/sbin/pkcsslotd
#end of startup script

子系统配置脚本询问系统,确定提供哪些设备并将它们自动放置到槽配置。假设设备依照其特定指令设置,子系统将反映在系统引导时检测到的配置。如果在 pkcs11_startup 脚本找不到设备,它们将不显示。通常设备不会被找到,因为在 /etc/rc.pkcs11 脚本被调用之前设备模块还没有装入。

PKCS #11 配置管理工具

openCryptoki 提供一个命令行程序( /usr/lib/pkcs11/methods/pkcsconf )来配置和管理系统内部支持的令牌。这个程序提供与运行 pkcsconf -? 命令相同的功能。这个功能包括令牌初始化、安全性主管 PIN 初始化和更改以及用户 PIN 初始化和更改。需要特定令牌的操作必须具有由 -c 标志指定的槽。可以通过指定不带特定槽号的 -s 选项查看系统内部提供的令牌列表。例如, 清单 2 显示了 pkcsconf 命令的选项并显示了令牌初始化前的系统槽信息:

清单 2
[root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -?
          /usr/lib/pkcs11/methods/pkcsconf: invalid option -- ?
          usage:  /usr/lib/pkcs11/methods/pkcsconf [-itsmMIupP]
         [-c slotnumber -U userPIN -S SOPin -n newpin]
                  -i display PKCS11 info
                  -t display token info
                  -s display slot info
                  -m display mechanism list
                  -M display shared memory
                  -I initialize token 
                  -u initialize user PIN
                  -p set the user PIN
                  -P set the SO PIN
          Output of the GetSlotInfo call 
          [root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -s
          Slot #0 Info
                  Description: Linux 2.4.2 Linux (ICA)
                  Manufacturer: Linux 2.4.2
                  Flags: 0x1
                  Hardware Version: 0.0
                  Firmware Version: 1.1
          An un-initialized ICA token, the information is obtained via the 
        C_GetTokenInfo call
          [root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -i
          PKCS#11 Info
                  Version 2.1 
                  Manufacuter: IBM AIX Software PKCS11          
                  Flags: 0x0  
                  Library Description: Meta PKCS11 LIBRARY              
                  Library VersionToken #0 Info:
                  Label: IBM ICA  PKCS #11               
                  Manufacturer: IBM Corp.                   
                  Model: IBM ICA         
                  Serial Number: 123             
                  Flags: 0x45
                  Sessions: -1/-1
                  R/W Sessions: -1/-1
                  PIN Length: 4-8
                  Public Memory: 0xFFFFFFFF/0xFFFFFFFF
                  Private Memory: 0xFFFFFFFF/0xFFFFFFFF
                  Hardware Version: 1.0
                  Firmware Version: 1.0
                  Time: 11:11:17 AM

这个程序还允许您为调试目的显示子系统共享内存段。另外,这个程序提供了对令牌的一些简单查询功能,譬如令牌信息和机制列表。

在应用程序使用 openCryptoki 子系统之前,必须执行下列初始化步骤(每个步骤都有示例代码):

1.初始化令牌:令牌在使用之前必须被初始化。这个过程的关键部分是指定一个唯一的标号。安全性主管 PIN 是必需的(所有 IBM
提供的令牌的缺省值是 87654321)。

注:当输入时,所有 PIN 在屏幕上都以星号显示。

清单 3
[root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -I -c 0
Enter the SO PIN: 87654321    
Enter a unique token label: DraegerICA
[root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -t -c 0
Token #0 Info:
Label: DraegerICA                      
Model: IBM ICA         
Serial Number: 123             
Flags: 0x45
Sessions: -1/-1
R/W Sessions: -1/-1
PIN Length: 4-8
Public Memory: 0xFFFFFFFF/0xFFFFFFFF
Private Memory: 0xFFFFFFFF/0xFFFFFFFF
Hardware Version: 1.0
Firmware Version: 1.0
Time: 11:14:17 AM

2. 设置安全性主管 PIN:谨慎的管理实践是让安全性主管在初始化令牌之后立即更改安全性主管 PIN。这个过程防止了未知个人再度初始化设备并除去所有已存储的对象(例如,密钥和证书)。

清单 4
[root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -P -c 0
Enter the SO PIN: 87654321
Enter the new SO PIN: fredrules
Re-enter the new SO PIN: fredrules

3. 设置用户 PIN(通过安全性主管)

清单 5
[root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -u -c 0
Enter the SO PIN: fredrules
Enter the new user PIN: billybob
Re-enter the new user PIN: billybob

4. 更改用户 PIN(通过用户)

清单 6
[root@draeger /root]# /usr/lib/pkcs11/methods/pkcsconf -p -c 0
Enter user PIN: billybob
Enter the new user PIN: 12345678
Re-enter the new user PIN: 12345678

应用程序与 openCryptoki

本节描述了如何使 openCryptoki 对于应用程序可用,并且提供了一个关于如何写此类应用程序的示例。

使 openCryptoki 对于应用程序可用

许多应用程序使用 PKCS #11 令牌。这些应用程序中的大多数必须被配置装入令牌的特定共享对象(DLL)。在 openCryptoki 环境下,只有一个模块( /usr/lib/pkcs11/PKCS11_API.so )必须装入,以访问当前在子系统运行的所有令牌。子系统支持多种令牌类型,每种令牌类型依照插件模块的实现指定对应于一个槽。

如果添加或除去设备,令牌所驻留的 PKCS #11 槽将更改。因为这个原因,当令牌已被初始化并且假设指定的槽并不总是包含期望的令牌时,应用程序应该通过所提供的令牌标号找到特定令牌。

对于与 PKCS #11 开发相关的应用程序特定配置信息,参阅应用程序文档。

编写应用程序

要开发使用 openCryptoki 的应用程序,您必须首先使用动态库调用装入共享对象。然后调用 C_GetFunctionList 。例如,下列例程装入共享程序库并得到后序调用的函数清单:

清单 7
CK_FUNCTION_LIST  *funcs;
int do_GetFunctionList( void )
{
   CK_RV            rc;
   CK_RV  (*pfoo)();
   void    *d;
   char    *e;
   char    f[]="/usr/lib/pkcs11/PKCS11_API.so";
   printf("do_GetFunctionList...
");
   d = dlopen(f,RTLD_NOW);
   if ( d == NULL ) {
      return FALSE;
   }
   pfoo = (CK_RV (*)())dlsym(d,"C_GetFunctionList");
   if (pfoo == NULL ) {
      return FALSE;
   }
   rc = pfoo(&funcs);
   if (rc != CKR_OK) {
      show_error("   C_GetFunctionList", rc );
      return FALSE;
   }
   printf("Looks okay...
");
   return TRUE;
}

一旦装入,应用程序必须调用 C_Initialize 函数。在前面的示例中,函数将通过下列行调用:

CK_C_INITIALIZE_ARGS cinit_args;
memset(&cinit_args,0x0,sizeof(cinit_args));
funcs->C_Initialize(&cinit_args);

参阅 RSA Labs 网站的 PKCS #11 规范(参阅 参考资料)以获得更多选项。

注: openCryptoki 要求允许操作系统线程。如果其它线程例程被传入,将被忽略。如果 no-os 线程变量在初始化变量结构中已经设置,对 C_Initialize 的调用将会失败。

样本程序

下列样本程序打印出当前在系统中使用的所有令牌和槽。如果您要构建样本程序,在该样本之后还必须包含 Makefile。

清单 8:Makefile
VPATH = ..
INCS    =   -I../. -I../../../../../include/pkcs11
CFLAGS  =   $(OPTLVL)  $(INCS) -DAPI -DDEV -D_THREAD_SAFE -DLINUX -DDEBUG -DSPINXPL 
CC = gcc
LD = gcc
LIBS = -ldl -lpthread 
OBJS    =   sample.o
.c.o: ; $(CC) -c $(CFLAGS) -o $@ $<
all:  sample
sample: $(OBJS)
    ${CC}  ${OBJS} $(LIBS) -o $@
TARGET = sample
build: $(TARGET)
clean:
    rm -f *.so *.o $(TARGET)

结束语

PKCS #11 是以统一方式建立与密码硬件设备接口的事实标准。openCryptoki 代表了在 Linux 上支持 IBM 密码硬件的 PKCS #11 的开放源码实现。这种支持为 Linux 添加了一种有价值的安全性特性,增强了它的吸引力和在企业市场上的商业用途。


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=21838
ArticleTitle=Linux 上的 PKCS #11 openCryptoki
publish-date=11012001