一个密码服务提供商(CSP)包括密码标准的实现和算法。最小情况下,一个CSP由一个实现了CryptoSPI(a system program interface)函数的动态链接库(DLL)组成。大多数的CSPs包含所有它们自己函数的实现。然而,一些CSPs,主要实现了由Windows服务控制管理者(Windows service control manager)管理的主要的基于Windows的服务程序。其他的用硬件来实现函数,比如一个智能卡(smard card)或者加密协处理器。如果一个CSP没有实现它自己的函数,DLL作为一个透传层,便利了操作系统和实际CSP实现的交流。
CSP体系结构
CSP概述
应用程序并不直接和CSP通信。相反,应用程序调用由操作系统的Advapi32.dll和Crypt32.dll文件提供的CryptoAPI函数。操作系统通过CryptoSPI(a system program interface)将过滤这些函数调用并把它们传递给合适的CSP函数。
一个CSP作者必须了解操作系统传递给CSP函数的参数的性质、规则和意义并且必须返回操作系统预期的值。
应用程序使用handles引用CSP内的数据对象。这些handles引用的对象包含key container、hash objects、session key和public/private key pair objects。这些handles使两边不透明,意思是,应用程序使用handle访问的数据对象并不是CSP使用的那个。因为各种各样的原因,操作系统层经常间接使用handles访问数据对象。
入口函数
所有自定义的CSPs必须支持以下DLL入口函数:
- CPAcquireContext
- CPCreateHash
- CPDecrypt
- CPDeriveKey
- CPDestroyHash
- CPDestroyKey
- CPEncrypt
- CPExportKey
- CPGenKey
- CPGenRandom
- CPGetHashParam
- CPGetKeyParam
- CPGetProvParam
- CPGetUserKey
- CPHashData
- CPHashSessionKey
- CPImportKey
- CPReleaseContext
- CPSetHashParam
- CPSetKeyParam
- CPSetProvParam
- CPSignHash
- CPVerifySignature
所有PROV_RSA_SCHANNEL和PROV_DH_SCHANNEL CSPs也必须支持以下DLL入口函数,这些入口函数对于其他自定义的CSPs是可选的:
这些函数构成了CryptoSPI system program interface。每个函数与CryptoAPI密码函数一致。
注意 所有这些函数必须用WINAPI关键字声明。
可存数据对象
CSP从一个会话到另一个会话存储公私钥到持久稳固的存储器。CSP用软件、加密格式、系统注册表可以实现存储这些密钥。有硬件成分的CSPs可能存储密钥对到防篡改的硬件。
存储密钥对的逻辑数据对象称为密钥容器(key containers)。CSP为每个使用CSP的用户或者客户端维护一个密钥容器。密钥容器可以存储每种CSP支持的类型的一对密钥。例如,Microsoft Base Cryptographic Provider支持两种密钥类型:交换密钥对(exchange key pair)和签名密钥对(digital signature key pair)。
一些密钥容器可以在任何时候打开(或者由一个单独的应用程序或者多个应用程序)。每个CryptoSPI的函数调用指定密钥容器用来做为这些函数参数的一个。下图是举例说明。
注意 在CryptoSPI上下文,HCRYPTPROV数据类型是一个指向CSP内特殊密钥容器的指针。在CryptoAPI上下文,CRYPTPROV数据类型可以用来指向密钥容器或者CSP。
Volatile数据对象
CPS在volaitle内存维护对话密钥(session key)对象和哈希对象(hash objects)。这些对象被CPGenKey和CPCreateHash创建出来,被CPDestoryKey和CPDestoryHash释放掉。当它们相应的密钥容器或者用户上下文被CPReleaseContext释放掉的时候,它们也必须被释放掉。
下图显示了在volatile内存维护的对象如何使用它们的指针访问。
CRYPT_VERIFYCONTEXT标志做为dwFlags参数传给CPAcquireContext用来使能创建volatile私钥的函数。PROV_RSA_SCHANNEL和PROV_DH_SCHANNEL CSPs都需要volatile私钥。
CSP线程问题
当调用微软的CSP,需要考虑一些有关线程的特性。
跨线程的同一密钥容器的建立或销毁是不支持的。除了使用CRYPT_NEWKEYSET或者CRYPT_DELETEKEYSE做为dwFlags参数的CryptAcquireContext函数通常是线程安全的。然而,CryptGenKey函数不是线程安全的因为它做了不能同步的文件I/O。详述这个问题,假设一个应用程序修改了一个密钥容器,例如建立一个新密钥对,导致不能同步的文件I/O操作跨越多个进程。因为这个原因,应用程序不应该使用默认的密钥容器。不需要访问密钥对的应用程序应该调用CryptAcquireContext并使用CRYPT_VERIFYCONTEXT做为dwFlags参数。因为没有进行文件I/O操作,所以好处是提高性能。同样,需要一个临时公私钥对的应用程序也可以使用CRYPT_VERIFYCONTEXT。多线程可以安全的执行任何CryptAcquireContext操作来对抗不同的容器。
多数算法和模式需要被解密的数据用它被加密的循序。这在一个多线程环境下是困难的任务,因为临界区的使用不会address the ordering issue。如果你在使用分组密码(也就是RC2、DES或者3DES)用ECB密码模式,那么这个问题就不是一个因素因为内部的密钥状态没有改变。然而,ECB不是默认是密码模式。CBC是默认的密码模式。使用CBC密码模式,内部密钥状态会改变。
在使用CryptGetUserKey函数获得一个密钥指针后,RSA签名和签名认证是线程安全的。
Cryptographic Provider Types
- PROV_RSA_FULL
- PROV_RSA_AES
- PROV_RSA_SIG
- PROV_RSA_SCHANNEL
- PROV_DSS
- PROV_DSS_DH
- PROV_DH_SCHANNEL
- PROV_FORTEZZA
- PROV_MS_EXCHANGE
- PROV_SSL