任务详情

密码引擎API的主要标准和规范包括:
1 微软的Crypto API
2 RAS公司的PKCS#11标准
3 中国商用密码标准:GMT 0016-2012 智能密码钥匙密码应用接口规范,GMT 0018-2012密码设备应用接口规范等

研究以上API接口,总结他们的异同,并以龙脉GM3000Key为例,写出调用不同接口的代码,提交博客链接和代码链接。
内容:
0 查找各种标准的原始文档,研究学习(至少包含Crypto API,PKCS#11,GMT 0016-2012,GMT 0018-2012)(5分)
1 总结这些API在编程中的使用方式(5分)
2 列出这些API包含的函数,进行分类,并总结它们的异同(10分)
3 以龙脉GM3000Key为例,写出调用不同接口的代码(Crypto API,PKCS#11,SKF接口),把运行截图加入博客,并提供代码链接(10分)

查找各种标准的原始文档,研究学习

CryptoAPI

PKCS#11

GM/T 0016-2012 智能密码钥匙密码应用接口规范

GM/T 0018-2012 密码设备应用接口规范

总结这些API在编程中的使用方式

CryptoAPI

微软的CryptoAPI是PKI推荐使用的加密 API。其功能是为应用程序开发者提供在Win32环境下使用加密、验证等安全服务时的标准加密接口。CryptoAPI处于应用程序和CSP(cryptographic service provider)之间。

CryptoAPI的编程模型同Windows系统的图形设备接口 GDI比较类似,其中加密服务提供者CSP等同于图形设备驱动程序 ,加密硬件(可选)等同于图形硬件,其上层的应用程序也类似,都不需要同设备驱动程序和硬件直接打交道。

CryptoAPI共有五部分组成:简单消息函数(Simplified Message Functions)、低层消息函数(Low-level Message Functions)、基本加密函数(Base Cryptographic Functions)、证书编解码函数(Certificate Encode/Decode Functions)和证书库管理函数(Certificate Store Functions)。其中前三者可用于对敏感信息进行加密或签名处理,可保证网络传输信心的私有性;后两者通过对证书的使用,可保证网络信息交流中的认证性。

密钥管理

  • 在CryptoAPI中,支持两种类型的密钥:会话密钥、公私钥对。会话密钥也称为对称密钥,用于对称密钥算法。为了保证密钥的安全性,在CryptoAPI中,这些密钥都保存在CSP内部,用户可以通过CryptExportKey以加密密钥形式导出。公私钥用于非对称加密算法。非对称加密算法主要用于加解密会话密钥和数字签名。在CryptoAPI中,一般来说,大多数CSP产生的密钥容器包含两对密钥对,一对用于加密会话密钥,称为交换密钥对,一对用于产生数字签名,称为签名密钥对。在CryptoAPI中所有的密钥都存储在CSP中,CSP负责密钥的创建,销毁,导入导出等操作。

数据编解码

  • CryptoAPI采用的编码方式为ASN.1,编码规则为DER,表示发送数据时先把数据抽象为ASN.1对象,然后使用DER编码规则把ASN.1对象转化为可传输的0,1串;接收方接收到数据后,利用DER解码规则把0,1串转化为ASN.1对象,然后把ASN.1对象转化为具体应用支持的数据对象。

数据加解密

  • 在CryptoAPI中约定加密较大数据块时,采用对称密钥算法。通过其封装好的加解密函数来实现数据加解密操作。

哈希和数字签名

  • 哈希和数字签名一般用于数据的完整性校验和身份鉴别。CryptoAPI中,通过其封装好的哈希与数字签名函数来实现相关操作。微软公司提供的CSP产生的数字签名遵循RSA标准(PKCS#6)

数字证书管理

  • 数字证书主要用于安全通信中的身份鉴别。CryptoAPI中,对数字证书的使用管理函数分为证书与证书库函数、证书验证函数两大部分。

PKCS#11

我们把Cryptoki 确定为应用程序与各种各样的便携式密码设备(基于智能卡、PCMCIA卡以及智能软盘)间的一种接口。
Cryptoki 的主要目标是一个低级程序接口,该接口将设备的细节抽象化,并把密码设备的通用模型—密码令牌,或简称令牌—提供给应用程序。
Cryptoki的通用模型如下图所示。模型从一个或多个必须执行某些密码操作的应用程序开始,以一个或多个密码设备结束(在密码设备上执行某些或全部操作)。一个用户可涉及也可不涉及一个程序。

Cryptoki 为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行。每个对应于一个物理阅读器或另一个设备接口的槽可包含一个令牌。当一台密码设备存在于阅读器中,一个令牌就存在于该槽中。当然,由于Cryptoki提供槽和令牌的逻辑视图,所以可能有其它的物理译码。多个槽可能共享一个阅读器。问题在于一个系统有相当多的槽,应用程序能连接到这些槽的其中任何一个或全部槽的令牌上。
Cryptoki的令牌逻辑视图是一个能存储对象和能执行密码函数的设备。Cryptoki定义如下三个对象:数据、证书和密钥。数据对象由应用程序定义。一个证书对象存储一个证书。一个密钥对象存储一个密码密钥。密钥可以是一个公共密钥、一个私钥或是一个保密密钥,每个种类的密钥在专用机制中使用其的辅助型。令牌的这种逻辑视图如下图所示:

SKF

针对支持国密算法USB KEY设备的应用,我国颁布一个行业标准《智能密码钥匙应用接口规范》(GM/T0016-2012),市面上销售的国密算法的USB KEY设备必须支持这个接口规范。因此,只要根据这个规范开发的应用程序,就可以兼容使用不同厂家及品牌的USB KEY产品。由于此规范中函数名称都以SKF开头,所以我们一般把按照此规范提供的设备开发接口库叫做SKF库或SKF接口。
智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间,如下图所示

一个设备中存在设备认证密钥和多个应用,应用之间相互独立。设备的逻辑结构如下图所示

应用由管理员PIN,用户PIN、文件和容器组成,可以存在多个文件和多个容器。每个应用维护各自的与管理员PIN和用户PIN相关的权限状态。
一个应用的逻辑结构如下图所示

容器中存放加密密钥对、签名密钥对和会话密钥。其中加密密钥对用于保护会话密钥,签名密钥对用于数字签名和验证,会话密钥用于数据加解密和MAC运算。容器中也可以存放与加密密钥对对应的加密数字证书和与签名密钥对对应的签名数字证书。其中,签名密钥对由内部产生,加密密钥对由外部产生并安全导入,会话密钥可由内部产生或者由外部产生并安全导入。

列出这些API包含的函数,进行分类,并总结它们的异同

CryptoAPI

密码服务提供者CSP函数

CryptoAPI的密码服务提供者函数主要包括6个函数。连接或断开CSP函数CryptAcquireContext、CryptReleaseContext,枚举CSP函数CryptEnumProviders,获得或设置默认CSP函数CryptGetDefaultProvider、CryptSetProvider,获取或设置CSP参数函数CryptGetProvParam、CryptSetProvParam。

连接CSP函数 CryptAcquireContext
  • 函数功能:连接CSP,获得指定CSP的密钥容器的句柄。
枚举CSP函数 CryptEnumProviders
  • 函数功能:枚举计算机上的所有CSP。此函数可以得到第一个或下一个可用的CSP。如果循环调用可以得到计算机上所有可用的CSP。
获得默认CSP函数 CryptGetDefaultProvider
  • 函数功能:获得系统默认的CSP。
设置默认CSP函数 CryptSetProvider
  • 函数功能:设置系统默认的CSP。
获得CSP参数属性函数 CryptGetProvParam
  • 函数功能:获得CSP各种参数属性。
设置CSP参数函数 CryptSetProvParam
  • 函数功能:设置CSP各种参数属性。
断开CSP函数 CryptReleaseContext
  • 函数功能:断开CSP,释放CSP句柄,和 CryptAcquireContext相对应。

示例枚举CSP并获得默认CSP的参数处理过程如下图所示

密钥的产生与交换函数

CryptoAPI密钥产生和交换函数主要有生成密钥函数 CryptGenKey、派生密钥函数CryptDeriveKey、销毁密钥函数CryptDestoryKey、复制密钥函数CryptDuplicateKey、导出密钥函数CryptExportKey、导入密钥函数CryptImportKey、获得密钥参数函数CryptGetKeyParam、设置密钥参数函数CryptSetKeyParam、产生随机函数 CryptGenRandom。

生成函数 CryptGenKey
  • 函数功能:产生一个随机的对称或非对称算法的密钥。
派生密钥函数 CryptDeriveKey
  • 函数功能:根据基础数据派生一对称密钥(会话密钥)。
销毁密钥函数 CryptDestroyKey
  • 函数功能:销毁密钥。
复制密钥函数 CryptDuplicateKey
  • 函数功能:复制一个密钥。产生一个密钥的拷贝,包括其状态。
导出密钥函数 CryptExportKey
  • 函数功能:从CSP导出密钥或密钥对。
导入密钥函数 CryptlmportKey
  • 函数功能:把BLOB数据导入的CSP。该函数可以导入会话密钥、公钥、或者公/私钥对。
获得密钥参数函数 CryptGetKeyParam
  • 函数功能:获得key句柄的各项参数。
获得密钥参数函数 CryptSetKeyParam
  • 函数功能:设置key句柄的各项参数。
获得密钥参数函数 CryptGenRandom
  • 函数功能:生成随机数。

示例密钥产生和交换实例处理过程如下图所示

数据的加密与解密函数

CryptoAPI利用CryptEncrypt函数实现数据加密,利用CryptDecrypt实现数据解密。调用这2个函数前必须指定一个密钥,这个密钥可以由CryptGenKey、CryptDeriveKey或CryptImportKey产生。也可用CryptSetKeyParam函数指定额外的加密参数。

数据加密函数 CryptEncrypt
  • 函数功能:使用hKey指定的密钥和算法加密数据。
数据解密函数 CryptDecrypt
  • 函数功能:使用hKey指定的密钥和算法对加密数据解密。

示例数据加密处理过程如下图所示

示例数据解密处理过程如下图所示

哈希和数字签名函数

CryptoAPI提供的哈希和数字签名函数包括创建哈希函数CryptCreateHash、销毁哈希CryptDestroyHash、复制哈希函数CryptDuplicateHash、获得哈希参数函数CryptGetHashParam,设置哈希参数函数CryptSetHashParam、哈希会话密钥函数 CryptHashSessionKey、哈希数据函数CryptHashData、对哈希签名函数CryptSignHash和对哈希验证签名函数CryptVerifySignature。

创建哈希函数 CryptCreateHash
  • 函数功能:创建哈希。
销毁哈希 CryptDestroyHash
  • 函数功能:销毁哈希对象。
复制哈希函数 CryptDuplicateHash
  • 函数功能:复制一个哈希对象。
获得哈希参数函数 CryptGetHashParam
  • 函数功能:获得哈希对象的参数。
设置哈希参数函数 CryptSetHashParam
  • 函数功能:设置哈希对象的参数。
哈希会话密钥函数 CryptHashSessionKey
  • 函数功能:对一个会话密钥进行哈希,把它加到指定的哈希对象中。
哈希数据函数 CryptHashData
  • 函数功能:对数据进行哈希操作,此函数可以反复调用。
对哈希签名函数 CryptSignHash
  • 函数功能:对哈希对象进行签名。
对哈希验证签名函数 CryptVerifySignature
  • 函数功能:验证哈希签名。

示例对数据签名和验证的流程如下图所示

证书和证书库函数

CryptoAPI证书和证书库函数主要包括打开证书库函数CertOpenStore、关闭证书库函数CertCloseStore、从证书库枚举证书函数CertEnumCertificatesInStore、从证书库查找证书函数CertFindCertificateInStore、创建证书句柄函数 CertCreateCertificateContext、释放证书句柄函数CertFreeCertificateContext、获得证书句柄属性函数CertGetCertificateContextProperty、设置证书句柄属性函数CertSetCertificateContextProperty和获得证书主题名称函数CertGetNameString。

打开证书库函数 CertOpenStore
  • 函数功能:根据证书库类型,打开证书库。
关闭证书库函数 CertCloseStore
  • 函数功能:关闭证书库。
从证书库枚举证书函数 CertEnumCertificateslnStore
  • 函数功能:枚举证书库中的证书。该函数通过循环调用,可以枚举证书库内的全部证书,上一次的返回,是下一次的pPrevCertContext,直到返回值为NULL。
从证书库查找证书函数 CertFindCertificatelnStore
  • 函数功能:从证书库中查找指定的证书。
创建证书句柄函数 CertCreateCertificateContext
  • 函数功能:由证书数据创建证书句柄。
释放证书句柄函数 CertFreeCertificateContext
  • 函数功能:释放证书句柄。
获得证书句柄属性函数 CertGetCertificateContextProperty
  • 函数功能:获得证书句柄属性。
设置证书句柄属性函数 CertSetCertificateContextProperty
  • 函数功能:设置证书句柄属性。
获得证书主题名称函数 CertGetNameString
  • 函数功能:从证书中获得主题或颁发者的名称。

示例枚举证书库并输出其属性的处理流程如下图所示

SKF

设备管理函数

访问控制函数

应用管理函数

文件管理函数

容器管理函数

密码服务函数


得到国密证书

根据国密标准,一种设备类型可以有多个设备(Device),每一个设备内可以有多个应用(Application),每一个应用里可以有多个容器(Container),每个容器里可以有一对证书(Certificate):签名证书和加密证书。

因此,应按照下列顺序依次调用相关接口:

  1. SKF_EnumDev(BOOL bPresent, LPSTR szNameList, ULONG *pulSize);
    调用这个方法用来遍历当前电脑上的设备,这个方法的第一个参数一般传TRUE,表示遍历的是插上的设备;第二个参数就是返回的设备名称列表;第三个参数是设备名称列表缓冲区长度。按照惯例,这个方法应该被调用两次:第一次szNameList传NULL,pulSize返回长度;第二次给szNameList分配pulSize长度,返回设备列表,每个设备的名称以单个’\0’结束,以双’\0’表示列表的结束。
  2. SKF_ConnectDev(LPSTR szName, DEVHANDLE *phDev);
    通过循环调用这个方法用来连接每一个具体的设备,szName为设备名,即上面方法得到的列表中的设备名;返回phDev为设备句柄。
  3. SKF_EnumApplication(DEVHANDLE hDev, LPSTR szAppNameList,ULONG *pulSize);
    得到设备句柄后,再通过此方法枚举得到设备里的应用列表。hDev为连接设备时返回的设备句柄;szAppNameList返回应用名称列表;pulSize是列表缓冲区长度。这个方法也是照例要调用两次,不再赘述。同样,每个应用的名称以单个’\0’结束,以双’\0’表示列表的结束。
  4. SKF_OpenApplication(DEVHANDLE hDev, LPSTR szAppName, HAPPLICATION phApplication);
    通过循环调用此方法打开应用列表里的每一个应用,hDev为连接设备时返回的设备句柄;szAppName是要打开的应用名称;phApplication为返回的应用句柄。
  5. SKF_EnumContainer(IN HAPPLICATIONhApplication, OUT LPSTRszContainerNameList, OUT ULONGpulSize)
    拿到应用句柄后,我们就可以用此方法遍历应用中的所有容器了。hApplication是应用句柄;szContainerNameList是返回的容器名称列表;pulSize是列表长度;后面的就不用了多说了。
  6. SKF_OpenContainer(HAPPLICATION hApplication,LPSTR szContainerName,HCONTAINER phContainer);
    循环调用此方法打开每一个容器。hApplication是应用句柄;szContainerName是要打开的容器名称;phContainer是返回的容器句柄。
  7. SKF_ExportCertificate(HCONTAINER hContainer, BOOL bSignFlag, BYTE pbCert, ULONG *pulCertLen);

最后就可以通过这个方法取得每个容器里的证书。hContainer是容器句柄;bSignFlag为导出的证书类型; TRUE表示导出的是签名证书;FALSE表示导出加密证书。pbCert为返回的证书数据,pulCertLen是证书数据的长度。同样需两次调用。

数字签名

在数字签名时,要指定签名所使用的证书。通过遍历本机上的证书,与签名用的证书进行对比,定位到签名证书在USBKEY中的位置,得到设备、应用和容器的句柄,然后使用证书的私钥进行签名。遍历对比的过程可参见上一节的内容。另外,由于数字签名会用到私钥,因此这里需要验证口令。

  1. SKF_VerifyPIN(HAPPLICATION hApplication, ULONG ulPINType, LPSTR szPIN, ULONG pulRetryCount);
    此方法用来验证证书所在应用的PIN码,及上面说的口令,为后面的签名取得权限。hApplication是应用句柄;ulPINType是PIN类型,可以为0是管理员账户,1为普通用户,这个参数一般选择1。szPIN值是PIN码,pulRetryCount为出错后返回的重试次数。
  2. SKF_ExportPublicKey(HCONTAINER hContainer, BOOL bSignFlag, BYTE pbBlob, ULONG* pulBlobLen);
    这个方法用来导出容器中的签名公钥,hContainer为证书所在容器句柄;bSignFlag 为导出密钥类型,TRUE表示导出签名公钥,FALSE表示导出加密公钥,这里选择TRUE;pbBlob为返回公钥的数据;pulBlobLen为数据的长度。这里这个方法可以不用调用两次,因为公钥结构是已知的,其长度也是固定的,因此可以直接为pbBlob分配固定长度的数据,以返回公钥。
  3. SKF_DigestInit(DEVHANDLE hDev, ULONG ulAlgID, ECCPUBLICKEYBLOB *pPubKey, unsigned char *pucID, ULONG ulIDLen, HANDLE *phHash);
    此方法进行杂凑(国密标准里把摘要称之为杂凑)运算初始化,并指定计算消息杂凑的算法。hDev为设备句柄;ulAlgID是杂凑算法标识,这里选择SGD_SM3(0x00000001),表明使用SM3算法;pPubKey为签名用证书公钥数据;pucID为签名者的ID值;ulIDLen是签名者的ID值的长度;phHash为返回的杂凑对象句柄。加入签名者ID值是SM2数字签名的一个重要特征,默认使用"1234567812345678"这个字符串值。
  4. SKF_Digest(HANDLE hHash, BYTE *pbData, ULONG ulDataLen, BYTE *pbHashData, ULONG *pulHashLen);
    初始化后,调用此方法进行数据杂凑运算。hHash是SKF_DigestInit方法返回的杂凑对象句柄; pbData为产生签名的原文,ulDataLen是原文数据的长度,pbHashData返回杂凑数据; pulHashLen返回杂凑结果的长度。同样,因为杂凑数据的长度都是固定的,这里同样可以为pbHashData事先分配固定长度,而不用再调用两遍。
    注意,如果进行杂凑的数据是分组的,那就得使用下面两个方法:
    SKF_DigestUpdate(HANDLE hHash, BYTE *pbData, ULONG ulDataLen);
    SKF_DigestFinal(HANDLE hHash, BYTE *pHashData, ULONG *pulHashLen);
    对每一组数据都使用SKF_DigestUpdate,最后调用SKF_DigestFinal返回杂凑值。当然,在数字签名运算中不存在分块计算签名的情况,所以这里也不会把数据分块杂凑。
  5. SKF_ECCSignData(HCONTAINER hContainer, BYTE *pbData, ULONG ulDataLen, PECCSIGNATUREBLOB pSignature);

最后调用此方法进行数字签名。hContainer用来签名的私钥所在容器句柄,也就是遍历对比证书得到的容器句柄;pbData是被签名的数据;ulDataLen是被签名数据长度,必须小于密钥模长; pbSignature为返回的签名值。

验证签名

  1. SKF_CreateContainer(HAPPLICATION hApplication, LPSTR szContainerName, HCONTAINER phContainer)
    调用此方法创建一个临时容器。hApplication为容器所在的应用句柄;szContainerName是ASCII字符串,表示所建立容器的名称,最大长度不能超过64字节;phContainer是返回所建立容器的容器句柄。
  2. SKF_ImportCertificate(HCONTAINER hContainer, BOOL bSignFlag, BYTE pbCert, ULONG ulCertLen);
    将签名用的证书导入到容器中。hContainer为容器句柄,即用上一方法创建的临时容器;bSignFlag为证书类型,TRUE表示签名证书,FALSE表示加密证书,这里选TRUE;pbCert,是证书数据;ulCertLen为证书数据长度;
  3. SKF_ExportPublicKey(HCONTAINER hContainer, BOOL bSignFlag, BYTE* pbBlob, ULONG* pulBlobLen);
    导出公钥。
  4. SKF_DigestInit(DEVHANDLE hDev, ULONG ulAlgID, ECCPUBLICKEYBLOB *pPubKey, unsigned char *pucID, ULONG ulIDLen, HANDLE *phHash);
    杂凑初始化。
  5. SKF_Digest(HANDLE hHash, BYTE *pbData, ULONG ulDataLen, BYTE *pbHashData, ULONG pulHashLen);
    杂凑运算。
  6. SKF_ECCVerify(DEVHANDLE hDev , ECCPUBLICKEYBLOB pECCPubKeyBlob, BYTE *pbData, ULONG ulDataLen, PECCSIGNATUREBLOB pSignature);
    进行签名验证。hDev是设备句柄;pECCPubKeyBlob是公钥数据结构,即第3步得到的公钥; pbData为待验证签名的数据;ulDataLen是待验证签名数据长度;pbSignature待验证的签名值。
  7. SKF_DeleteContainer(HAPPLICATION hApplication, LPSTR szContainerName);
    删除临时容器。hApplication为容器所在的应用句柄;szContainerName为容器名称。

3.以龙脉GM3000Key为例,写出调用不同接口的代码(Crypto API,PKCS#11,SKF接口),把运行截图加入博客,并提供代码链接

SKF接口

  • 龙脉密码钥匙驱动实例工具等\mToken-GM3000\skf\samples\windows\EncryptData\EncryptData.sln

Crypto API

  • 龙脉密码钥匙驱动实例工具等\mToken-GM3000\csp\samples\CryptAPI\VC\EncryptDecryptFile\EncryptFile.sln

PKCS#11

  • 龙脉密码钥匙驱动实例工具等\mToken-GM3000\pkcs11\windows\samples\PKCStest\PKCStest.sln
    • DES
    • DES3
    • RC2
    • RC4
    • RSA
    • AES