赞
踩
数字签名
和非对称加密类似,数字签名通过调用CryptSignMessage这一个方法即可实现,这一个方法里将摘要运算和私钥加密摘要两步都包含进去了。
BOOL CryptSignMessage(
PCRYPT_SIGN_MESSAGE_PARA pSignPara,
BOOL fDetachedSignature,
DWORD cToBeSigned,
const BYTE * [] rgpbToBeSigned,
DWORD [] rgcbToBeSigned,
BYTE *pbSignedBlob,
DWORD *pcbSignedBlob
)
pSignPara是签名参数,比如可做如下设置:
CRYPT_SIGN_MESSAGE_PARA SigParams;
SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SigParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;//编码方式
SigParams.pSigningCert = pCertContext;//签名所用证书的上下文
SigParams.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;//摘要算法用SHA1
SigParams.HashAlgorithm.Parameters.cbData = NULL;
SigParams.cMsgCert = 1;// CryptSignMessage生成的是标准的PKCS7格式签名,支持多用户签名,这里指定签名用户数,也就是rgpMsgCert数组元素个数。当然,绝大部分情况下只有一个用户签名。
SigParams.rgpMsgCert = &pCertContext;//包含所有签名证书的数组,必须将pSigningCert指向的证书上下文对象添加到这个数组里
SigParams.cAuthAttr = 0;//用户认证属性数组元素个数,也就是rgAuthAttr元素个数,没有的话就传0
SigParams.rgAuthAttr = NULL;// 用户认证属性数组
SigParams的其他属性值可以参看相关文档,一般情况下都设置为0或NUL;fDetachedSignature标识签名结果是否与原文分离,一般设置为TRUE;cToBeSigned 是待签名原文的个数,也就是rgpbToBeSigned和rgcbToBeSigned的元素个数;注意,如果fDetachedSignature为FALSE,即签名和原文不分离的情况下,cToBeSigned只能是1,也就是只能有一个原文进行签名;rgpbToBeSigned是待签名的原文数组;rgcbToBeSigned是待签名原文长度的数组;pbSignedBlob为返回的PKCS7格式签名结果,如果fDetachedSignature为TRUE,则结果为纯签名;否则结果里包含签名和原文数据;pcbSignedBlob是pbSignedBlob的字节长度。
同样,每一次签名时此方法应调用两次,第一次pbSignedBlob传NULL,pcbSignedBlob返回签名结果的实际长度,第二次调用时为pbSignedBlob分配pcbSignedBlob的字节长度。第二次调用一定要用实际长度分配空间,否则即使签名成功,验证时也可能失败。
方法成功返回TRUE,失败返回FALSE,调用GetLastError可返回具体错误信息。
签名验证
根据签名结果是否与原文分离,验证签名的方法也相应有两种:
签名与原文分离1. CryptAcquireContext
2. BOOL CryptVerifyDetachedMessageSignature(
PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
DWORD dwSignerIndex,
const BYTE *pbDetachedSignBlob,
DWORD cbDetachedSignBlob,
DWORD cToBeSigned,
const BYTE * [] rgpbToBeSigned,
DWORD [] rgcbToBeSigned,
PCCERT_CONTEXT *ppSignerCert
)
pVerifyPara是验证参数,可以做如下设置:
CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;//编码方式
VerifyParams.hCryptProv = hProv;// CryptAcquireContext返回的CSP句柄
VerifyParams.pfnGetSignerCertificate = NULL;//用于返回签名证书的回调函数,如果为NULL,则使用缺省回调函数,即从签名数据里返回
VerifyParams.pvGetArg = NULL;//上述回调函数的参数
dwSignerIndex是被验证的签名的索引值,从0开始。通过循环调用此方法并递增dwSignerIndex,可以验证签名数据中的所有签名。当验证结果返回FALSE,并且用GetLastError返回的错误是CRYPT_E_NO_SIGNER,所有签名已验证完毕;
pbDetachedSignBlob为待验证的签名数据;cbDetachedSignBlob为待验证的签名数据长度;cToBeSigned为生成签名的原文个数;rgpbToBeSigned是原文数组;rgcbToBeSigned是原文长度的数组;ppSignerCert返回签名证书的上下文,同样这个上下文对象在使用完毕后要用CertFreeCertificateContext释放。如果不需要返回,ppSignerCert参数传NULL。
方法返回值为TRUE时表明验证成功,否则验证失败,可以调用GetLastError得到具体错误信息。和签名本身有关的常见错误有:
签名与原文不分离1. CryptAcquireContext
2. BOOL CryptVerifyMessageSignature(
PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
DWORD dwSignerIndex,
const BYTE *pbSignedBlob,
DWORD cbSignedBlob,
BYTE *pbDecoded,
DWORD *pcbDecoded,
PCCERT_CONTEXT *ppSignerCert
)
CryptVerifyMessageSignature方法的前两个和最后一个参数与CryptVerifyDetachedMessageSignature方法一致;pbSignedBlob为待验证签名数据,里面包含签名和原文;cbSignedBlob为签名数据长度;pbDecoded为返回的原文;pcbDecoded为原文的长度;如果想返回原文,那CryptVerifyMessageSignature需调用两次,第一次pcbDecoded传NULL,pcbDecoded返回其需要的实际长度,第二次为pcbDecoded分配实际长度空间。
在返回值方面,CryptVerifyMessageSignature方法多了一种常见错误码ERROR_MORE_DATA(0xEA),表示为pbDecoded分配的空间不够。
另外,调用CryptoAPI的程序处理的数据经常是BASE64编码字符串类型,但CryptoAPI输入输出参数都是字节流类型,所以在实际开发时需要进行BASE64和字节数组之间的转换。
到此就把常用的CryptoAPI功能接口介绍完毕了。完整的CryptoAPI函数接口大家可以参考相关文档和头文件WinCrypt.h,有任何问题欢迎在评论区交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。