Solaris 加密框架是一种体系结构,可使 Solaris 操作系统中的应用程序使用或提供加密服务。所有与框架的交互都基于 RSA Security Inc. 的 PKCS#11 加密令牌接口 (Cryptographic Token Interface, Cryptoki)。PKCS#11 是 RSA Laboratories(RSA Security Inc. 的研究机构)开发的一种产品。本章介绍 Solaris 加密框架的以下主题:
获取加密服务的应用程序、库或内核模块称作消费方,通过加密框架向消费方提供加密服务的应用程序称作提供者,或称作插件。用来实现加密操作的软件称作机制。机制不只是算法,还包括算法的应用方式。例如,应用于验证机制的 DES(数据加密标准)算法被视为一个单独的机制,应用于逐块加密的 DES 则是另一个机制。
令牌是可以执行加密的设备的抽象术语。此外,令牌可以存储要用在加密操作中的信息。一个令牌可以支持一个或多个机制。令牌可以代表硬件,例如加速器板中的部件。仅代表软件的令牌称作软令牌。令牌可以插入到插槽中,插槽也是一种物理比喻。插槽是使用加密服务的应用程序的连接点。
除了提供者的特定插槽以外,Solaris 实现还提供一个名为元插槽的特殊插槽。元插槽是 Solaris 加密框架库 (libpkcs11.so) 的一个组件。元插槽充当一个虚拟插槽,它具有框架中安装的所有令牌和插槽的组合功能。实际上,元插槽使应用程序通过单个插槽与任何可用的加密服务透明地连接。当应用程序请求加密服务时,元插槽将指向最适合的插槽,这简化了插槽的选择过程。在某些情况下可能需要一个不同的插槽,此时,应用程序必须显式地执行单独的搜索。元插槽由系统自动启用,而且只能由系统管理员通过显式的操作来禁用。
会话是令牌与使用加密服务的应用程序之间的连接。PKCS #11 标准使用两种对象:令牌对象和会话对象。会话对象是暂时的对象,即它只在会话期间存在。在会话结束之后仍存在的对象称作令牌对象。
令牌对象的缺省位置是 $HOME/.sunw/pkcs11_softtoken。或者,可以将令牌对象存储到 $SOFTTOKEN_DIR/pkcs11_softtoken 中。专用令牌对象由个人识别码 (personal identification numbe, PIN) 保护。要创建或更改令牌对象,需要对用户进行验证,除非用户访问的是专用令牌对象。
加密框架是 Solaris OS 的一部分,它提供 Sun Microsystems, Inc. 和各个第三方供应商的加密服务。加密框架提供以下各种服务:
消息加密和消息摘要
消息验证代码 (Message authentication code, MAC)
数字签名
用来访问加密服务的应用程序编程接口 (Application programming interface, API)
用来提供加密服务的服务提供者接口 (Service provider interface, SPI)
用来管理加密资源的管理命令
下图提供了加密框架的概述。该图中的浅灰色阴影表示加密框架的用户级部分,深灰色阴影表示加密框架的内核级部分,带有斜条纹的背景表示专用软件部分。
加密框架的组件如下所述:
libpkcs11.so -该框架通过 RSA Security Inc. 的 PKCS#11 加密令牌接口 (Cryptographic Token Interface, Cryptoki) 提供访问权限。需要将应用程序链接到 libpkcs11.so 库,该库实现 RSA PKCS#11 v2.11 标准。
可插接式接口-可插接式接口是 PKCS #11 加密服务的服务提供者接口 (service provider interface, SPI),这些服务由 Sun Microsystems, Inc. 和第三方开发者提供。提供者是用户级库,可以通过能够从硬件或软件使用的加密服务实现。
pkcs11_softtoken.so -一个专用共享对象,其中包含由 Sun Microsystems, Inc. 提供的用户级加密机制。pkcs11_softtoken(5) 库可用来实现 RSA PKCS#11 v2.11 标准。
pkcs11_kernel.so -用来访问内核级加密机制的专用共享对象。pkcs11_kernel(5) 可用来实现 RSA PKCS#11 v2.11 规范。pkcs11_kernel.so 为插入到内核的服务提供者接口中的加密服务提供一个 PKCS#11 用户接口。
/dev/crypto 伪设备驱动程序-要使用内核级加密机制的专用伪设备驱动程序。提供此信息的目的在于避免意外删除伪设备驱动程序。
内核编程接口-用于加密服务的内核级消费方的接口。IPSec(Internet 协议安全)协议和 Kerberos GSS 机制是典型的加密消费方。
只有与 Sun Microsystems, Inc. 签订了特殊的合同才能使用此接口。有关更多信息,请发电子邮件至 solaris-crypto-api@sun.com。
服务提供者接口-内核级加密服务的提供者使用的 SPI。这些服务可以在硬件或软件上实现。要使用 SPI,提供者必须从 Solaris 内核导入特殊的例程。这些例程允许模块和设备驱动程序注册和删除服务,还将状态更改通知给框架。该框架还要求提供者导出某些例程。所导出的例程允许加密框架的消费方和其他组件向提供者发送请求。
Sun HW 和 SW 加密提供者-由 Sun Microsystems, Inc. 提供的内核级加密服务。HW 是指硬件加密服务(如加速器板),SW 是指提供加密服务的内核模块(如对于加密算法的实现)。
elfsign -提供给加密服务的第三方提供者的实用程序。elfsign 用于从 Sun 请求证书,还允许提供者对插入到 Solaris 加密框架中的二进制文件(即 elf 对象)进行实际签名。
/dev/cryptoadm 伪设备驱动程序-一种专用的伪设备驱动程序,由 cryptoadm(1M) 用来管理内核级加密机制。提供此信息的目的在于避免意外删除伪设备驱动程序。
cryptoadm -可供管理员管理加密服务的用户级命令。使用 cryptoadm 执行的典型任务就是列出加密提供者及其功能,使用该命令还可以根据安全策略禁用和启用加密机制。
本节介绍可插入到 Solaris 加密框架中的四种应用程序的开发要求。
包括 <security/cryptoki.h>。
所有的调用都只通过 PKCS #11 接口执行。
链接到 libpkcs11.so。
库不应当调用 C_Finalize() 函数。
有关更多信息,请参见第 9 章,编写用户级加密应用程序和提供者。
设计要独立使用的提供者。尽管由提供者共享的对象不必是应用程序所链接到的具有完整功能的库,但是提供者中必须存在所有必需的符号。假设提供者将要在 RTLD_GROUP 和 RTLD_NOW 模式下由 dlopen(3C) 打开。
在共享对象中创建 PKCS #11 Cryptoki 实现。此共享对象中应当包括必需的符号,而不要依赖于消费方应用程序。
强烈建议(但并非必需)为数据清除提供 _fini() 例程。如果应用程序或共享库同时装入 libpkcs11 和其他提供者库,则需要使用此方法来避免在 C_Finalize() 调用之间产生冲突。请参见避免在用户级提供者中出现数据清除冲突。
应用 Sun Microsystems, Inc. 提供的证书。请参见申请提供者签署证书。
使用证书和 elfsign 对二进制文件进行签名。请参见签署提供者。
按照 Sun 约定对共享对象进行打包。请参见附录 F,打包和签署加密提供者。
包括 <sys/crypto/common.h> 和 <sys/crypto/api.h>。
所有的调用都通过内核编程接口执行。
包括 <sys/crypto/common.h> 和 <sys/crypto/api.h>。
导入注册、注销和提供状态所必需的例程。
导出必需的例程,以便为内核加密框架提供入口点。
导出具有受支持算法说明的数据结构。
创建可装入的内核模块。
应用 Sun Microsystems, Inc. 提供的证书。请参见申请提供者签署证书。
使用证书和 elfsign 对二进制文件进行签名。请参见签署提供者。
按照 Sun 约定对内核模块进行打包。请参见附录 F,打包和签署加密提供者。
插入到加密框架中的用户级库应当提供 _fini() 函数。卸载用户级库时,装入器会调用 _fini() 函数。_fini() 函数是确保所有的清除操作都在恰当的时间正确完成所必需的。系统不假设那些使用 libpkcs11 的库调用 C_Finalize(),因为 libpkcs11 是共享库,有可能正由应用程序使用。
要提供 _fini() 函数,需要在可重定位对象的程序数据部分中创建一个 .fini 部分。.fini 部分提供运行时终止代码块。请参见《链接程序和库指南》。以下代码样例演示了如何设计 .fini 部分。
#pragma fini(pkcs11_fini) static void pkcs11_fini(); /* [... (other library code omitted)] */ static void pkcs11_fini() { (void) pthread_mutex_lock(&pkcs11mutex); /* If CRYPTOKI is not initialized, do not clean up */ if (!initialized) { (void) pthread_mutex_unlock(&pkcs11mutex); return; } (void) finalize_routine(NULL_PTR); (void) pthread_mutex_unlock(&pkcs11mutex); }