实验一-密码引擎-加密API研究
密码引擎API的主要标准和规范包括: 1 微软的Crypto API 2 RAS公司的PKCS#11标准 3 中国商用密码标准:GMT 0016-2012 智能密码钥匙密码应用接口规范,GMT 0018-2012密码设备应用接口规范等 研究以上API接口,总结他们的异同,并以龙脉GM3000Key为例,写出调用不同接口的代码,提交博客链接和代码链接。
一、微软的Crypto API
Windows CryptoAPI是Microsoft 公司提出的安全加密应用服务框架,也是PKI推荐使用的加密 API。它提供了在Win32 环境下使用认证、编码、加密和签名等安全服务时的标准加密接口,用于增强应用程序的安全性与可控性。
CryptoAPI 提供的功能主要有:密钥管理、数据加密和解密、数字签名和验证、证书管理、可信根证书管理、数据编码和解码、数字证书编码和解码、PKCS#7 标准格式编码和解码等。
在微软加密服务体系中,加密服务相关的所有操作都在CSP实现,它是真正实行加密相关服务的独立模块,既可以由软件实现也可以由硬件实现,但是必须符合CryptoAPI接口的规范。每个CSP必须包含有一个动态链接库和一个签名文件,签名文件用于保证底层的CSP 的安全性,CryptoAPI 接口在加载每个CSP 时,需要验证CSP 的签名,如果签名无效,则拒绝加载,CSP 的签名由微软公司签发。同时,每个CSP都有一个名字和一个类型,名字必须唯一的,这样便于CryptoAPI找到对应的CSP。加密服务标准被分为不同的家族,每种家族包含自己的一系列数据格式和协议,不同的家族有不同的数据格式或协议等。在CryptoAPI中,每种CSP类型代表不同的家族。目前已经有9种CSP类型,并且还在增长,不同类型支持的密钥交换算法、签名算法、对称加密算法和Hash算法等如下表所示。
CSP类型 |
交换算法 |
签名算法 |
对称加密算法 |
Hash算法 |
PROV_RSA_FULL |
RSA |
RSA |
RC2 |
MD5 |
PROV_RSA_SIG |
none |
RSA |
none |
MD5 |
PROV_RSA_SCHANNEL |
RSA |
RSA |
RC4 |
MD5 |
PROV_DSS |
DSS |
none |
DSS |
MD5 |
PROV_DSS_DH |
DH |
DSS |
CYLINK_MEK |
MD5 |
PROV_DH_SCHANNEL |
DH |
DSS |
DES |
MD5 |
PROV_FORTEZZA |
KEA |
DSS |
Skipjack |
SHA |
PROV_MS_EXCHANGE |
RSA |
RSA |
CAST |
MD5 |
PROV_SSL |
RSA |
RSA |
Varies |
Varies |
Crypto API的结构框架
CPS函数接口
名称 |
描述 |
连接函数 |
|
CPAcquireContext |
为应用程序创建一个上下文 |
CPGetProvParam |
获取CSP相关信息 |
CPReleaseContext |
释放CPAcquireContext获取的上下文 |
CPGetProvParam |
设置CSP相关参数 |
密钥生成和交换函数 |
|
CPDeriveKey |
从一个数据散列中生成一个会话密钥,保证生成的密钥互不相同 |
CPDestroyKey |
释放密钥句柄,释放后密钥句柄无效,密钥将不能再被访问 |
CPDuplicateKey |
创建密钥的拷贝 |
CPExportKey |
从CSP密钥容器中导出密钥 |
CPImportKey |
从一个Blob中导入密钥到CSP容器中 |
CPGenKey |
生成密钥或者密钥对 |
CPGenRandom |
生成随机数 |
CPSetKeyParam |
设置密钥属性 |
CPGetKeyParam |
获取密钥属性 |
CPGetUserKey |
获取密钥容器中持久密钥对 |
数据加密函数 |
|
CPEncrypt |
加密明文 |
CPDecrypt |
解密密文 |
散列和数字签名函数 |
|
CPCreateHash |
初始化散列对象 |
CPDestroyHash |
删除散列对象 |
CPDublicateHash |
创建散列对象拷贝 |
CPSetHashParam |
设置散列对象属性 |
CPGetHashParam |
获取散列对象属性 |
CPHashData |
散列输入数据 |
CPHashSessionKey |
散列一个会话密钥 |
CPSignHash |
签名一个散列对象 |
CPVerifySignature |
验证一个散列对象 |
实践代码:
#include "tool.h" #include <windows.h> #include <wincrypt.h> #include <stdio.h> #include <QDebug> BOOL CalHash(unsigned char* src, unsigned char* hash, int len) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。 LPCTSTR pszContainerName = TEXT("Hash");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_AES, //确定密钥的类型 0)) { printf("CSP Create Success\n"); }else //不成功的处理段 { if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。 { printf("A new key container has been created.\n"); }else { printf("CSP Create Fail\n"); return FALSE; } } } HCRYPTHASH hHash; //创建hash对象 if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hHash)) { printf("CryptCreateHash fail!"); return FALSE; } if(!CryptHashData( hHash, (BYTE*)src, len, 0 )) { printf("CryptHashData fail!"); return FALSE; } DWORD dwHashLen= 16; if(!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE*)hash, &dwHashLen, 0)) { printf("CryptGetHashParam fail!"); return FALSE; } CryptDestroyHash(hHash); CryptReleaseContext(hCryptProv, 0); return TRUE; } BOOL Encrypt(unsigned char* src, unsigned char* dest, char* passwd ) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。 LPCTSTR pszContainerName = TEXT("Encrypt");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_AES, //确定密钥的类型 0)) { printf("CSP Create Success\n"); } else { //不成功的处理段 if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器 { printf("A new key container has been created.\n"); } } HCRYPTHASH hCryptHash; //创建hash对象 if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hCryptHash)) { printf("CryptCreateHash fail!"); return FALSE; } //用输入的密码作哈稀散列 if(!CryptHashData( hCryptHash,lse { printf("CSP Create Fail\n"); return FALSE; } } (BYTE*)passwd, strlen(passwd), 0)) { printf("CryptHashData fail!"); return FALSE; } //用哈稀散列生成会话密钥 HCRYPTKEY hCryptKey; if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey )) { printf("CryptDeriveKey fail!"); return FALSE; } //对文件进行加密,hCryptKey已经与加密算法相关联了CALG_AES_128 unsigned char buf[16]; DWORD lenght = 16; memcpy(buf,src,16); if(!CryptEncrypt(hCryptKey, NULL,//如果数据同时进行散列和加密,这里传入一个散列对象 0,//如果是最后一个块为TRUE 0, (BYTE*)buf,//输入被加密的数据,输出加密数据 &lenght,//输入输入数据长度,输出加密后数据长度 16 )) { qDebug() << "errre"; qDebug() << GetLastError(); } memcpy(dest,buf,16); CryptDestroyHash(hCryptHash); CryptReleaseContext(hCryptProv, 0); return TRUE; } BOOL Decrypt(unsigned char* src, unsigned char* dest, char* passwd ) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。 LPCTSTR pszContainerName = TEXT("Decrypt");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_AES, //确定密钥的类型 0)) //常设为0,还有些其他的类型,请看MSDN { printf("CSP Create Success\n"); } else { //不成功的处理段 if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。 { printf("A new key container has been created.\n"); } else { printf("CSP Create Fail\n"); return FALSE; } } } HCRYPTHASH hCryptHash; //创建hash对象 if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hCryptHash)) { printf("CryptCreateHash fail!"); return FALSE; } //用输入的密码作哈稀散列 if(!CryptHashData( hCryptHash, (BYTE*)passwd, strlen(passwd), 0 )) { printf("CryptHashData fail!"); return FALSE; } //用哈稀散列生成会话密钥 HCRYPTKEY hCryptKey; if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey )) { printf("CryptDeriveKey fail!"); return FALSE; } CryptDestroyHash(hCryptHash); //对文件进行加密,hCryptKey已经与加密算法相关联了CALG_AES_128 unsigned char buf[16]; DWORD lenght = 16; memcpy(buf,src,16); CryptDecrypt(hCryptKey, NULL,//如果数据同时进行散列和加密,这里传入一个散列对象 0,//如果是最后一个块为TRUE 0, (BYTE*)buf,//输入被加密的数据,输出加密数据 &lenght//输入输入数据长度,输出加密后数据长度 ); memcpy(dest,buf,16); CryptReleaseContext(hCryptProv, 0); return TRUE; } BOOL Sign(unsigned char* src, unsigned char* dest, int len) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。 LPCTSTR pszContainerName = TEXT("Decrypt");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_FULL, //确定密钥的类型 0)) //常设为0,还有些其他的类型,请看MSDN { printf("CSP Create Success\n"); } else { //不成功的处理段 if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。 { printf("A new key container has been created.\n"); } else { printf("CSP Create Fail\n"); return FALSE; } } } HCRYPTKEY hKey; //创建一个密钥句柄 if(CryptGetUserKey( // CryptGetUserKey是获取一个密钥句柄的函数,成功返回TRUE hCryptProv, //指定容器的CSP模块句柄 AT_SIGNATURE, //指定私钥的类型 &hKey)) //原来接收获取的密钥句柄 { printf("A signature key is available.\n"); } else { printf("No signature key is available.\n"); if(GetLastError() == NTE_NO_KEY) // NTE_NO_KEY意味着密钥不存在,下面就生成一个密钥 { printf("The signature key does not exist.\n"); printf("Create a signature key pair.\n"); if(CryptGenKey( // CryptGenKey生成一个密钥 hCryptProv, //指定CSP模块的句柄 AT_SIGNATURE, //对于公钥密码系统,生成一个私钥和一个公钥,这个参数指定了这个密钥是公钥,于是生成了一个密码对。如果不是公钥系统,则指定了密码算法,具体看MSDN。 0, //指定了生成密钥的类型,这个参数的说明挺多的,想获取更为详尽的资料请看MSDN。 &hKey)) { printf("Created a signature key pair.\n"); } else { printf("Created a signature key fail.\n"); return FALSE; } } else { printf("An error other than NTE_NO_KEY "); return FALSE; } } HCRYPTHASH hHash; if(!CryptHashData( hHash, (BYTE*)src, len, 0 )) { printf("CryptHashData fail!"); return FALSE; } DWORD lenght = 32; if(!CryptSignHash(hHash,AT_SIGNATURE,NULL,CRYPT_TYPE2_FORMAT,(BYTE*)dest,&lenght)) { printf("CryptSignHash fail!"); return FALSE; } if(hKey) //将密钥句柄销毁 { if(!(CryptDestroyKey(hKey))) { printf("Error during CryptDestroyKey."); } hKey = NULL; } CryptDestroyHash(hHash); CryptReleaseContext(hCryptProv, 0); return TRUE; } char *qstoc(const QString Qstr) { QByteArray ba = Qstr.toLatin1(); char *c_str; c_str = (char *)malloc(ba.length() + 1); memset(c_str, 0, ba.length()); memcpy(c_str, ba.data(), ba.length()); c_str[ba.length()] = '\0'; return c_str; }
二、RSA公司的PKCS#11
PKCS#11是公钥加密标准Public-Key Cryptography Standards中的一份子,由RSA实验室发布。
PKCS#11标准定义了与密码令牌的独立于平台的API,API本身命名为Cryptoki,这个API已经发展成为一个通用的加密令牌的抽象层。
PKCS#11主要是应用于智能卡和HSM。
PKCS#11为使用加密Token的应用程序提供统一的编程接口,独立于设备,屏蔽加密设备的复杂性,应用程序可以方便地更换设备。
Cryptoki:Cryptographic Token Interface Standard 密码令牌接口标准,应用程序与各种各样便携式密码设备间的一种接口。
设备的种类和所支持的能力的种类取决于专用的Cryptoki库。该标准只定义库的接口,不定义库的实现,接口实现由设备商提供。
Cryptoki主要目标是一个低级程序接口,将设备的细节抽象化,并把密码设备的通用模型-密码令牌提供给应用程序。第二目标是资源共享,单个设备能为一个以上的应用程序共享。
Cryptoki为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行,密码设备可以按照某一命令集执行某些密码操作,这些命令通常通过标准的设备驱动程序来实现,Cryptoki的作用就是屏蔽这些硬件的差异。
P11接口函数
1、通用接口
2、槽和令牌管理
3、会话管理
4、对象管理
6、解密函数
7、消息摘要
8、签名和MAC
9、验证签名和MAC
10、密钥管理
三、GTM 0016-2012
这个标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。
层次关系:智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间
设备的应用结构:一个设备中存在设备认证密钥和多个应用,应用之间相互独立。设备的逻辑结构如下图所示:
应用由管理员PIN、用户PIN、文件和容器组成,可以存在多个文件和多个容器。
密码服务系列函数
四、GTM 0018-2012
本标准的目标是为公钥密码基础设施应用体系框架下的服务类密码设备制定统一的应用接口标准,通过该接口调用密码设备,向上层提供基础密码服务。为该类密码设备的开发、使用及检测提供标准依据和指导,有利于提高该类密码设备的产品化、标准化和系列化水平。
在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机、密码卡、智能密码终端等设备组成,通过该标准规定的密码设备应用接口向通用密码服务层提供基础密码服务,如下图所示:
接口函数
设备管理类函数
- 打开设备:SDF_OpenDevice
- 关闭设备:SDF_CloseDevice
- 创建会话:SDF_OpenSession
- 关闭会话:SDF_CloseSession
- 获取设备信息:SDF_GetDeviceInfo
- 产生随机数:SDF_GenerateRandom
- 获取私钥使用权限:SDF_GetPrivateKeyAccessRight
- 释放私钥使用权限:SDF_ReleasePrivateKeyAccessRight
密钥管理类函数
- 导出 RSA 签名公钥:SDF_ExportSignPublicKey_RSA
- 导出 RSA 加密公钥:SDF_ExportEncPublicKey_RSA
- 产生RSA非对称密钥对并输出:SDF_GenerateKeyPair_RSA
- 生成会话密钥并用内部RSA公钥加密输出:SDF_GenerateKeyWithIPK_RSA
- 生成会话密钥并用外部RSA公钥加密输出:SDF_GenerateKeyWithEPK_RSA
- 导人会话密钥并用内部RSA私钥解密:SDF_ImportKeyWithISK_RSA
- 基于 RSA 算法的数宇信封转换:SDF_ExchangeDigitEnvelopeBaseOnRSA
- 导出 ECC 签名公钥:SDF_ExportSignPublicKey_ECC
- 导出 ECC 加密公钥:SDF_ExportEncPublicKey_ECC
- 产生ECC非对称密钥对并输出:SDF_GenerateKeyPair_ECC
- 生成会话密钥并用内部ECC公钥加密输岀:SDF_GenerateKeyWithIPK_ECC
- 生成会话密钥并用外部ECC公钥加密输出:SDF_GenerateKeyWithEPK_ECC
- 导入会话密钥并用内部ECC私钥解密:SDFJmportKeyWithlSKJECC
- 生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
- 计算会话密钥:SDF_GenerateKey WithECC
- 产生协商数据并计算会话密钥:SDF_GenerateAgreementDataAndKeyWithECC
- 基于 ECC算法的数字信封转换:SDF_ExchangeDigitEnvelopeBaseOnECC
- 生成会话密钥并用密钥加密密钥加密输出: SDF_GenerateKeyWithKEK
- 导入会话密钥并用密钥加密密钥解密:SDF_ImportKeyWithKEK
- 销毁会话密钥:SDF_DestroyKey
非对称算法运算类函数
- 外部公钥 RSA 运算:SDF_ExternalPublicKeyOperation_RSA
- 内部公钥 RSA 运算:SDF_InternalPublicKeyOperation_RSA
- 内部私钥 RSA 运算:SDF_InternalPrivateKeyOperation_RSA
- 外部密钥 ECC 验证:SDF_ExternalVerify_ECC
- 内部密钥 ECC 签名:SDF_InternalSign_ECC
- 内部密钥 ECC 验证:SDF_InternalVerify_ECC
- 外部密钥 ECC 加密:SDF_ExternalEncrypt_ECC
对称算法运算类函数
- 对称加密:SDF_Encrypt
- 对称解密:SDF_Decrypt
- 计算MAC:SDF_CalculateMAC
杂凑运算类函数
- 杂凑运算初始化:SDF_HashInit
- 多包杂凑运算:SDF_HashUpdate
- 杂凑运算结束:SDF_HashFinal