智能卡CSP 的设计与实现方法
随着智能卡功能的不断完善,卡片运算速度和存贮功能的不断加强,在对安全性要求较高的领域,智能卡的应用开始越来越广泛。其中作为硬件数字证书使用,也是智能卡的一项重要功能。智能卡作为硬件级的加密设备,如何同当前使用最为广泛的windows操作系统进行无缝连接,需要开发智能卡读卡器硬件、读卡器驱动程序、智能卡CSP 等一系列软硬件设施,本文将主要针对其中的智能卡CSP 开发。
1 CSP 简介
加密服务提供者Cryptographic Service Provider(简称CSP)是Windows 操作系统加密体系的重要组成部分,它提供了一组标准API 函数(CryptoAPI)供应用程序调用,如IE 使用SSL 访问网站、Outlook 发送加密邮件等,均会调用到CryptoAPI 函数。智能卡作为一种硬件级的加密设备,要实现和windows 操作系统的无缝连接,使应用程序能够通过CryptoAPI 这套标准函数使用智能卡设备, 就必定要针对该种设备开发CSP 服务程序。智能卡设备CSP 在系统中的位置如图1 所示。
2 CSP 中的容器
CSP 使用容器来管理密钥,以RSA 密钥为例,一个容器中可以存在一对RSA 交换密钥和一对RSA 签名密钥。一个智能卡中可以有多个容器。结构如图2 所示。Windows 系统中一般会存在多个CSP,既有微软自己的纯软件型CSP,也可能有数个不同厂商的软硬件加密设备的CSP。应用程序可以通过CryptoAPI 函数的来指定使用哪个CSP 以及该CSP 中的哪个容器。
3 CSP 在智能卡中的密钥存贮结构
3.1 智能卡中私钥的特点
在CSP 中私钥的作用主要是用来做解密或签名。智能卡这种设备的一个重要特点是私钥可以设定为读禁止,私钥不能被从智能卡中读出。当需要用私钥进行解密或者签名时,被解密或签名的数据必须先送入智能卡,由智能卡中的处理器对数据做解密或签名,解密或签名后的数据再出智能卡返回计算机中。整个过程中私钥不能被计算机读出,解密或签名的过程是在智能卡中进行的,保证了私钥的不可复制特性,避免了黑客攻入计算机,将私钥远程拷贝走的可能。
3.2 私钥、公钥和证书的不同保护级别
使用私钥时,智能卡需要验证保护该私钥的PIN 码,只有PIN 码验证正确的情况下才能使用私钥。但智能卡中的证书和公钥则一般不需要PIN 码保护,以保证使用过程中的灵活性。在CryptoAPI 的SILENT 模式中,公钥可以随时被读出。另外当智能卡插入到连接计算机的读卡器中时,一般都需要将智能卡中的证书导入到windows 系统的证书库中,因IE 浏览器不能直接识别智能卡中的证书,它需要从windows 系统的证书库中去读证书。这些情况下均需要让智能卡不经过PIN 码验证,就能使智能卡中的公钥和证书被读出。
3.3 CSP 密钥容器的存贮结构设计
3.3.1 CSP 密钥容器存贮结构图
图3 为CSP 密钥容器存贮结构图。
.3.2 公开目录(DDF):如图3 所示,公开目录(DDF)下的ADF 子目录下存放RSA 加密公钥及相应证书、RSA 签名公钥及相应证书,容器名称为ADF 目录的名称,可以同时存在多个容器。公开目录(DDF)、容器目录(ADF)、公钥、证书都不设置PIN 码保护,公钥和证书可以随时可以被从智能卡中读出。
3.3.3 私钥目录(DDF):如图3 所示,私钥目录(DDF)下的ADF 子目录下存放RSA 加密密钥对中的私钥和RSA 签名密钥对中的私钥,ADF 目录名称与对应公钥所在的ADF 目录名称相同。私钥目录(DDF)设置PIN 码保护,要使用该目录的子目录下的私钥,必须首先通过私钥目录(DDF)的PIN 码验证。
3.3.4 容器名称:图3 中的私钥目录(DDF)下的容器目录(ADF)名称必须和公开目录(DDF)下的容器目录(ADF)名称对应,比如私钥目录(DDF)下的
容器目录1 和公开目录(DDF)下的容器目录1 的名称必须相同,因为它们实际上是代表着同一个容器名。
3.3.5 容器索引文件:容器索引文件存放着智能卡中的所有容器名称, 并且指明容器名称和容器目录(ADF) 之间的关系。每次调用CSP 的CPAcquireContext函数时,该函数都需要从这个文件中获取智能卡中已有的所有容器名称。容器索引文件的结构可以用如下方式表示:
## 容器名称1# 容器目录1(ADF)## 容器名称2# 容器目录2(ADF)##......#......##
4 CSP 软件架构的设计与实现
4.1 CSP 软件架构的种类
CSP 从整体上看主要有上下文环境对象、密钥对象、哈希对象三种数据结构。在开发CSP 的过程有几种方法来实现对这三种数据结构对象的管理,具体如下:
结构对象的管理,具体如下:
1) 上下文环境对象在CSP 中实现,密钥对象和哈希对象交给微软的纯软件型CSP 来管理。
2) 上下文环境对象和密钥对象在CSP 中实现,哈希对象交给微软的纯软件型CSP 来管理。
3) 上下文环境对象、密钥对象和哈希对象都在CSP 中实现。
其中第3 种方法实现CSP 的复杂性最高,但也最为灵活,本文主要探讨这种方法。由于在CSP 开发中一般都用C 语言或C++语言来实现,因此约定以下用到的数据结构定义均使用C++语言来表述。
4.2 CSP 中几个基本的对象类型分析
通过分析微软定义的CSP 25 个基本函数,可以发现CSP 的上下文环境对象、密钥对象、哈希对象是以HCRYPTPROV、HCRYPTKEY和HCRYPTHASH 三种类型存在的。
HCRYPTPROV 对象类型的作用是串联起整个CSP 的上下文环境。该对象一般由CPAcquireContext 函数产生,由CPReleaseContext函数终止。
HCRYPTKEY 对象类型起到密钥句柄的作用。其存在周期一般是从密钥的产生或者密钥导入开始,经历密钥的使用,最后到密钥句柄被释放的过程。
HCRYPTHASH 对象类型起到哈希句柄的作用。其存在周期一般是从哈希的产生,到哈希的使用,最后是哈希句柄被释放的过程。
{$page$}
4.3 三种对象类型的设计与实现
在CSP 的具体设计与实现中,HCRYPTPROV 对象类型、HCRYPTKEY对象类型和HCRYPTHASH 对象类型之间的相互关系如图4 所示。
ProvQueue 链表为CSP 上下文环境句柄链表,KeyQueue 链表为密钥句柄链表,HashQueue 链表为哈希句柄链表。
CProvContext 为CSP 上下文环境类、CCryptKey 为密钥句柄类、CCryptHash 为哈希句柄类,这几个类的具体设计见下文。
4.3.1 CSP 上下文环境对象的实现
CSP 的上下文环境由HCRYPTPROV 对象类型实现,在实现过程中可以定义一个CProvContext 的类,具体定义如下:
该类包含有在当前上下文环境中使用的容器名ContainerName,通过SetProvParam 成员函数可以对当前上下文环境属性进行设置,通过GetProvParam 成员函数可以得到当前上下文环境的属性。将该类强制转换成HCRYPTPROV 类型,即可实现CSP 上下文环境的数据类型。
如果在同一个进程中通过CryptoAPI 的CryptAcquireContext 函数同时打开多个不同的容器,此时就会有多个CProvContext 类实例,因此需要通过链表来管理多个实例。这里定义一个ProvQueue 的链表,在同一个进程中每当打开一个容器或新建一个容器时,就产生一个CProvContext 类的实例,将该实例加入到ProvQueue 链表中,通过prevContext 指针指向前一个实例, 通过nextContext 指针指向后一个实例。
CSP 25 个函数中都有CSP 的上下文环境句柄,25 个函数之间的关系可以通过这个句柄得到联系。比如调用CPEncryt 函数时,首先检查CPEncryt 传入HCRYPTPROV 句柄是否在ProvQueue 中存在,如果存在,则ProvQueue 链表中对应的该条记录即为当前的上下文环境句柄,该记录中包含有当前的容器名、相关的密钥队列(KeyQueue)和哈希队列(HashQueue),从密钥队列(KeyQueue)中可以获取CPEncryt 函数需要使用的密钥对象。对象之间的关系可参见图4。
4.3.2 HCRYPTKEY 密钥对象类型
HCRYPTKEY 密钥对象类型可以定义一个CCryptKey 的类,在使用过程中将该类强制转换成HCRYPTKEY 类型,该类具体定义如下:
该类包含有当前密钥所对应的算法(KeyAlgid)、dwFlags 参数以及当前密钥隶属于的CSP 上下文环境对象(pProContext)。通过SetKeyParam 成员函数可以对当前密钥属性进行设置,通过GetKeyParam 成员函数可以得到当前密钥的属性。
在同一个CSP 上下文环境对象中可以同时产生多个不同的密钥句柄,因此需要通过密钥句柄链表来管理这些密钥句柄。定义一个KeyQueue 的链表,在同一个CSP 上下文环境对象中每当产生一个新的密钥或导入一个新的密钥时,就会产生一个CCryptKey类的实例,将该实例加入到KeyQueue 链表中,通过prevCryptKey 指针指向前一个实例, 通过nextCryptKey 指针指向后一个实例。
通过图4,可以看到密钥与密钥链表之间的关系,具体使用某个密钥时需要先从KeyQueue 链表中找到对应的密钥句柄。比如调用CPEncryt 函数时,先检查CPEncryt 函数传入的HCRYPTKEY 句柄是否在KeyQueue 链表中已经存在,如果存在,则KeyQueue 链表中对应的该条记录即为当前的密钥句柄,该记录中包含有当前密钥的算法(KeyAlgid)、dwFlags 等参数。
4.3.3 HCRYPTHASH 哈希对象类型
HCRYPTHASH 哈希对象类型的定义和HCRYPTKEY 密钥对象类型的定义相似,定义一个CCryptHash 的类,在使用过程中将该类强制转换成HCRYPTHASH 类型,该类具体定义如下:
该类中包含有当前哈希所对应的算法(HashAlgid)、dwFlags 参数以及当前哈希隶属于的CSP 上下文环境对象(pProContext)。通过SetHashParam 成员函数可以对当前哈希属性进行设置,通过GetHashParam 成员函数可以得到当前哈希的属性。
在同一个CSP 上下文环境对象中可以同时产生多个不同的哈希句柄,因此需要通过哈希句柄链表来管理这些哈希句柄。定义一个HashQueue 的链表,在同一CSP 上下文环境中每当产生一个新的哈希时,就会产生一个CCryptHash 类的实例,将该实例加入到HashQueue 链表中,通过prevCryptHash 指针指向前一个实例, 通过nextCryptHash 指针指向后一个实例。
通过图4,可以看到哈希与哈希链表之间的关系,具体使用某个哈希时需要先从HashQueue 链表中找到对应的哈希句柄。比如调用CPHashData 函数时, 先检查CPHashData 函数传入的HCRYPTHASH 句柄是否在HashQueue 链表中已经存在, 如果存在,则HashQueue 链表中对应的该条记录即为当前的哈希句柄,该记录中包含有当前哈希的算法(HashAlgid)、dwFlags 等参数。
5 综述
综上所述,由于微软对于CSP 只是定义了基本的函数接口,具体在CSP 的设计与实现过程中有很强的灵活性,除了本文提到的几种CSP 的设计和实现方法外,可能还会有其它的CSP 设计和实现方法,CSP 中密钥存贮结构的设计方法也有很多。随着智能卡技术的不断发展,以及智能卡在各行各业中应用的普及,各行各业对智能卡CSP 功能上的要求可能会有不同,安全性方面的要求可能也会有差异,因此CSP 软件架构如何设计,对应的密钥存贮结构如何设计,主要还是看CSP 的具体用途 。