实验一-密码引擎-加密API研究
任务详情
密码引擎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分)
查找各种标准的原始文档,研究学习(至少包含Crypto API,PKCS#11,GMT 0016-2012,GMT 0018-2012)
Ⅰ.微软的Crypto API
1.CryptoAPI系统架构由五个主要功能区域组成
基本密码功能
证书编码/解码功能
证书存储功能
简化的消息功能
低级消息功能
1.1 基本密码功能
①服务提供者函数
应用程序使用服务提供者函数来连接和断开一个 CSP。下图为主要的 API:
②密钥的产生和交换函数
密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。下图为主要的一些函数:
③编码/解码函数
有一些编码/解码函数,他们可以用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。
④数据加密/解密函数
这些函数支持数据的加密/解密操作。CryptEncrypt 和 CryptDecrypt 要求在被调用前指定一个密钥。
这个密钥可以由 CryptGenKey、CryptDeriveKey 或 CryptImportKey 产生。创建密钥时要指定加密算法。CryptSetKeyParam 函数可以指定额外的加密参数。
⑤哈希和数字签名函数
完成计算哈希、创建和校验数字签名
函数详解
获得 CSP 密钥容器句柄
BOOL WINAPI CryptAcquireContext( HCRYPTPROV *phProv, LPCTSTR pszContainer, LPCTSTR pszProvider, DWORD dwProvType, DWORD dwFlags );
这个函数是用来取得指定 CSP 密钥容器句柄,以后的任何加密操作就是针对此 CSP 句柄而言。函数首先查找由 dwProvType 和 pszProvider 指定的 CSP,如果找到了 CSP,函数就
查找由此 CSP 指定的密钥容器。由适当的 dwFlags 标志,这个函数就可以创建和销毁密钥容器,如果不要求访问私钥的话,也可以提供对 CSP 临时密钥容器的访问。
BOOL WINAPI CryptReleaseContext( HCRYPTPROV hProv, DWORD dwFlags );
hProv:[in]由CryptAcquireContext 获得的 CSP 句柄。
dwFlags:[in]保留。必须为 0。
HCRYPTPROV hCryptProv; if(CryptAcquireContext( hCryptProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) CryptReleaseContext(hCryptProv, NULL);
此函数释放 CSP 的句柄。对于每一次调用,CSP 的引用计数都减 1。当引用计数为 0时,CSP 上下文就会被系统释放变成无效句柄,以后针对此 CSP 句柄的函数不再可用。此函数并不销毁密钥容器或密钥对。
枚举 CSP
BOOL WINAPI CryptEnumProviders( DWORD dwIndex, DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, LPTSTR pszProvName, DWORD *pcbProvName );
此函数得到第一个或下一个可用的 CSP。如果使用循环,就可以得到计算机上所有可用
的 CSP。
/--------------------------------------------------------------------
//声明初始化数据
DWORD cbName;
DWORD dwType;
DWORD dwIndex=0;
CHAR *pszName;
//循环枚举 CSP.
dwIndex = 0;
while(CryptEnumProviders(
dwIndex,
NULL,
0,
&dwType,
NULL, // 如果为 NULL,则 cbName 返回 CSP 名称所需字节数
&cbName //
))
{
//--------------------------------------------------------------------
//
//动态分配内存.
if (!(pszName = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, cbName)))
{
printf("ERROR - LocalAlloc failed!");
exit(1);
}
//--------------------------------------------------------------------
// 获得 CSP 名称
if (CryptEnumProviders(
dwIndex++,
NULL,
0,
&dwType,
pszName,
&cbName
))
{
printf ("%4.0d %s\n",dwType, pszName);
}
else
{
printf("ERROR - CryptEnumProviders");
exit(1);
}
LocalFree(pszName);
} // 循环结束
获得 CSP 参数
BOOL WINAPI CryptGetProvParam( HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags );
此函数获得 CSP 的各种参数。
//-----------------------------------------------------------------
//
HCRYPTPROV hCryptProv;
BYTE pbData[1000];
DWORD cbData;
//-------------------------------------------------------------------
//缺省 CSP 名称
cbData = 1000;
if(CryptGetProvParam(
hCryptProv,
PP_NAME,
pbData,
&cbData,
0))
{
printf("CryptGetProvParam succeeded.\n");
printf("Provider name: %s\n", pbData);
}
else
{
printf("Error reading CSP name. \n");
exit(1);
}
//--------------------------------------------------------------------
cbData = 1000;
if(CryptGetProvParam(
hCryptProv,
PP_CONTAINER,
pbData,
&cbData,
0))
{
printf("CryptGetProvParam succeeded. \n");
printf("Key Container name: %s\n", pbData);
}
else
{
printf("Error reading key container name. \n");
exit(1);
}
创建哈希
BOOL WINAPI CryptCreateHash( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash );
始化哈希数据流。它创建并返回了一个 CSP 哈希对象的句柄。此句柄由CryptHashData 和 CryptHashSessionKey 来调用
BOOL WINAPI CryptHashData( HCRYPTHASH hHash, BYTE *pbData, DWORD dwDataLen, DWORD dwFlags );
此函数把一段数据加入到指定的哈希对象中去
BOOL WINAPI CryptGetHashParam( HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags );
此函数得到指定哈希对象的数据
BOOL WINAPI CryptDestroyHash( HCRYPTHASH hHash );
此函数销毁由 hHash 指定的哈希对象。当一个哈希对象被销毁后,它对程序来说不可用
BOOL WINAPI CryptDeriveKey( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey );
此函数从一基本数据值中派生会话密钥。函数保证当 CSP 和算法相同时,从相同基本数据值中产生的密钥是唯一的。
BOOL WINAPI CryptDestroyKey( HCRYPTKEY hKey );
此函数释放密钥句柄
BOOL WINAPI CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwBufLen
);
此函数用于加密数据。加密数据所需要的算法由 hKey 的密钥指定。
BOOL WINAPI CryptDecrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen
);
此函数对由 CryptEncrypt 加密过的数据进行解密。
签名/验证
BOOL WINAPI CryptSignMessage(
PCRYPT_SIGN_MESSAGE_PARA pSignPara,
BOOL fDetachedSignature,
DWORD cToBeSigned,
const BYTE *rgpbToBeSigned[ ],
DWORD rgcbToBeSigned[ ],
BYTE *pbSignedBlob,
DWORD *pcbSignedBlob
);
此函数对指定数据进行哈希,然后对哈希值进行签名,然后对原始消息和签名哈希进行编码。
//--------------------------------------------------------------------
…
#define MY_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define SIGNER_NAME L"Insert_signer_name_here"
#define CERT_STORE_NAME L"MY"
void HandleError(char *s);
void main(void)
{
// 系统证书库句柄
HCERTSTORE hStoreHandle;
//--------------------------------------------------------------------
// 待签名的消息
BYTE* pbMessage =
(BYTE*)"CryptoAPI is a good way to handle security";
//--------------------------------------------------------------------
DWORD cbMessage = strlen((char*) pbMessage)+1;
//--------------------------------------------------------------------
// 证书的上下文
PCCERT_CONTEXT pSignerCert;
CRYPT_SIGN_MESSAGE_PARA SigParams;
DWORD cbSignedMessageBlob;
BYTE *pbSignedMessageBlob;
DWORD cbDecodedMessageBlob;
BYTE *pbDecodedMessageBlob;
CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
//--------------------------------------------------------------------
const BYTE* MessageArray[] = {pbMessage};
DWORD MessageSizeArray[1];
MessageSizeArray[0] = cbMessage;
//--------------------------------------------------------------------
//
printf("Begin processing. \n");
printf(" The message to be signed is\n-> %s.\n",pbMessage);
//--------------------------------------------------------------------
// Open a certificate store.
if ( !( hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_STORE_NAME)))
{
HandleError("The MY store could not be opened.");
}
//--------------------------------------------------------------------
//
//得到证书的上下文,此证书必须能访问签名者的私钥
if(pSignerCert = CertFindCertificateInStore(
hStoreHandle,
MY_TYPE,
0,
CERT_FIND_SUBJECT_STR,
SIGNER_NAME,
NULL))
{
printf("The signer's certificate was found.\n");
}
else
{
HandleError( "Signer certificate not found.");
}
//--------------------------------------------------------------------
//初始化签名结构
SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SigParams.dwMsgEncodingType = MY_TYPE;
SigParams.pSigningCert = pSignerCert;
SigParams.HashAlgorithm.pszObjId = szOID_RSA_MD5;
SigParams.HashAlgorithm.Parameters.cbData = NULL;
SigParams.cMsgCert = 1;
SigParams.rgpMsgCert = &pSignerCert;
SigParams.cAuthAttr = 0;
SigParams.dwInnerContentType = 0;
SigParams.cMsgCrl = 0;
SigParams.cUnauthAttr = 0;
SigParams.dwFlags = 0;
SigParams.pvHashAuxInfo = NULL;
SigParams.rgAuthAttr = NULL;
//--------------------------------------------------------------------
//
// 首先得到 BLOB 的大小
if(CryptSignMessage(
&SigParams, // Signature parameters
FALSE, // Not detached
1, // Number of messages
MessageArray, // Messages to be signed
MessageSizeArray, // Size of messages
NULL, // Buffer for signed message
&cbSignedMessageBlob)) // Size of buffer
{
printf("The size of the BLOB is %d.\n",cbSignedMessageBlob);
}
else
{
HandleError("Getting signed BLOB size failed");
}
//--------------------------------------------------------------------
// 分配 BLOB 的内存.
if(!(pbSignedMessageBlob =
(BYTE*)malloc(cbSignedMessageBlob)))
{
HandleError("Memory allocation error while signing.");
}
//--------------------------------------------------------------------
//
if(CryptSignMessage(
&SigParams, //
FALSE, //
1, // 消息数量
MessageArray, // 待签名的消息
MessageSizeArray, // 消息大小
pbSignedMessageBlob, // 缓冲区
&cbSignedMessageBlob)) // 缓冲区大小
{
printf("The message was signed successfully. \n");
}
else
{
HandleError("Error getting signed BLOB");
}
//--------------------------------------------------------------------
// 验证签名信息
//--------------------------------------------------------------------
// 初始化 VerifyParams 结构.
VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType = MY_TYPE;
VerifyParams.hCryptProv = 0;
VerifyParams.pfnGetSignerCertificate = NULL;
VerifyParams.pvGetArg = NULL;
//--------------------------------------------------------------------
//
if(CryptVerifyMessageSignature(
&VerifyParams, //.
0, //
pbSignedMessageBlob, //.
cbSignedMessageBlob, //
NULL, //
&cbDecodedMessageBlob, //.
NULL)) // Pointer to signer certificate.
{
printf("%d bytes need for the buffer.\n",cbDecodedMessageBlob);
}
else
{
printf("Verification message failed. \n");
}
//--------------------------------------------------------------------
// 为缓冲区分配内存.
if(!(pbDecodedMessageBlob =
(BYTE*)malloc(cbDecodedMessageBlob)))
{
HandleError("Memory allocation error allocating decode BLOB.");
}
//--------------------------------------------------------------------
//
// 得到缓冲区的大小
if(CryptVerifyMessageSignature(
&VerifyParams, // Verify parameters.
0, // Signer index.
pbSignedMessageBlob, // Pointer to signed BLOB.
cbSignedMessageBlob, // Size of signed BLOB.
pbDecodedMessageBlob, // Buffer for decoded message.
&cbDecodedMessageBlob, // Size of buffer.
NULL)) // Pointer to signer certificate.
{
printf("The verified message is \n-> %s \n",pbDecodedMessageBlob);
}
else
{
printf("Verification message failed. \n");
}
//--------------------------------------------------------------------
//
if(pbSignedMessageBlob)
free(pbSignedMessageBlob);
if(pbDecodedMessageBlob)
free(pbDecodedMessageBlob);
if(pSignerCert)
CertFreeCertificateContext(pSignerCert);
if(CertCloseStore(
hStoreHandle;
CERT_CLOSE_STORE_CHECK_FLAG))
{
printf("The store closed and all certificates are freed. \n");
}
else
{
printf("Store closed after signing -- \n"
"not all certificates, CRLs or CTLs were freed");
}
…
证书和证书库函数
一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了
这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了
对库获得、枚举、验证和使用证书库里的信息。
以下就是这些函数:
维护函数
证书函数
证书撤销列表函数
证书信任列表函数
扩展属性函数
证书验证函数
使用 CTL 的函数
证书链验证函数
消息函数
低级消息函数
简化消息函数
辅助函数
数据管理函数
数据转换函数
增强密钥用法函数
密钥标示函数
证书库回调函数
OID 支持函数
PFX 函数
Ⅱ.PKCS#11
PKCS#11(简称P11)就是针对密码设备的接口指令标准。P11模型中重要的概念之一是slot,也称为槽。一个slot为一个密码设备对象。某个打开的slot会话称之为session。Session之间存在不同的验证权限。而同一个slot的不同的session之间存在操作的互相影响性,同时在某些状况下,权限会发生同步。另外一个重要的概念是对象。P11中支持几种重要的对象,如公钥、私钥、对称密钥,数据对象等。
CSP接口标准:CSP接口标准为微软所颁发,在windows操作系统上通行。CSP中重要的概念是容器(container)。一个容器中具有一对公私钥。而证书却是这一对密钥的附加属性了。
通用模型
Cryptoki的通用模型如下图所示。模型从一个或多个必须执行某些密码操作的应用程序开始,以一个或多个密码设备结束(在密码设备上执行某些或全部操作)。一个用户可涉及也可不涉及一个程序。
Cryptoki 为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行。每个对应于一个物理阅读器或另一个设备接口的槽可包含一个令牌。当一台密码设备存在于阅读器中,一个令牌就存在于该槽中。当然,由于Cryptoki提供槽和令牌的逻辑视图,所以可能有其它的物理译码。多个槽可能共享一个阅读器。问题在于一个系统有相当多的槽,应用程序能连接到这些槽的其中任何一个或全部槽的令牌上。
密码设备可以按照某一命令集执行某些密码操作,这些命令通常要经过标准设备驱动程序,例如PCMCIA卡服务程序或槽服务程序。Cryptoki 使每个密码设备看起来逻辑上很象其它设备,而不管什么技术实现的。因此,应用程序不必直接与设备驱动器接口(或甚至不必知道包括那些设备);Cryptoki 隐藏了这些细节。的确,基础设备完全能用软件来实现,(例如,在一个服务器上运行的处理程序),不须专用硬件。
Cryptoki 或许可以作为支持接口功能的库来实现,而应用程序则与该库连接。应用程序可以直接与Cryptoki 连接,或者,Cryptoki 是一个所谓的“共享”库(或动态连接库),在这种情况下,应用程序动态地连接库。用Microsoft Windows和OS/2操作系统可以比较容易地生成数据库,并且在UNIX和DOS中也可相对容易地生成“共享”库。
由于新库可以使用,所以动态方法有许多优点;但从安全的角度来说,也有一些缺点。要特别指出的是,如果库能较容易地被替换,攻击者有可能用恶意制造的假库取而代之,以截取用户的PIN。即使编码签名技术能防止许多动态连接的安全危险,从安全角度来说,一般采用直接连接。总之,应用程序和Cryptoki 库之间的程序设计接口是相同的。
设备的种类和所支持的能力的种类将取决于专用Cryptoki 库。本标准只定义库的接口,不定义库的特征。要特别指出的是,并不是所有的库支持这个接口(因为不是所有的令牌支持所有的机制)中定义的机制(算法)。并且库也许只支持可使用的所有密码设备的一个子集。(当然,可以预料更多更好的设备种类将被开发,以支持多种令牌,而不是单个供应商提供的令牌。)只要开发出应用程序,就会形成Cryptoki 的接口、标准数据库和令牌“轮廓”
Ⅲ.GMT 0016-2012
结构模型
智能密码钥匙应用接口位于智能密码钥匙应用程序与设备之间
一个设备中存在设备认证密钥和多个应用,应用之间相互独立。
应用由管理员PIN、用户PIN、文件和容器组成,可以存在多个文件和多个容器。
每个应用维护各自的与管理员PIN和用户PIN相关的权限状态。
一个应用的逻辑结构如图所示
密码服务
错误代码
Ⅳ.GMT 0018-2012
设备管理:
打开设备:SDEOpenDerice
关闭设备:SDF_ Cias eDevice
创建会话:SDF_ OpenSesion
关闭会话: SDF _CloseSession
获取设备信息:SDF _GetDeviceInfo
产生随机数:SDF_enerateRandom
获取私钥使用权限: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私钥解密:SDF_ ImportKeyWithISK_ ECC
生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
计算会话密钥:SDF_GenerateKeyWithECC
产生协商数据并计算会话密钥: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_ nternalSign_ ECC
内部密钥ECC验证:SDF_ InternalVerify_ ECC
外部密钥ECC加密:SDF_ ExternalEncrypt_ECC
对称算法运算类函数
对称加密:SDF_Encrypt
对称解密:SDF_Decrypt
计算MAC:SDF_CalculateMAC
杂凑运算类函数
杂凑运算初始化:SDF_HashInit
多包杂凑运算:SDF_HashUpdate
杂凑运算结束:SDF_HashFinal
用户文件操作类函数:
创建文件:SDF_CreatFile
读取文件:SDF_ReadFile
写文件:SDF_WriteFile
删除文件:SDF_DeleteFile
总结异同:
这三类标准中都包含加解密的函数,并且自成体系。
具体函数名称有些许不同,但大同小异。
还有GM涉及到了设备的管理,所以有设备管理接口还有访问控制函数。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?