CryptoAPI调用指南(四)——数字签名及验证

七、 数字签名
和非对称加密类似,数字签名通过调用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可返回具体错误信息。
八、 签名验证
根据签名结果是否与原文分离,验证签名的方法也相应有两种:

  • 签名与原文分离
  • CryptAcquireContext
  • 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得到具体错误信息。和签名本身有关的常见错误有:
错误码说明
E_INVALIDARG(0x80070057)数据编码方式无效。目前只支持PKCS_7_ASN_ENCODING和 X509_ASN_ENCODING_TYPE
CRYPT_E_UNEXPECTED_MSG_TYPE(0x8009200A)非数字签名。
CRYPT_E_NO_SIGNER(0x8009200E)没有未验证的签名了。
NTE_BAD_ALGID(0x80090008)未知或不支持的算法。
NTE_BAD_SIGNATURE(0x80090006)无效的签名。也就是原文或(和)签名被改动造成的验证失败。
  • 签名与原文不分离
  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,有任何问题欢迎在评论区交流。
CryptoAPI是一组用于加密和解密数据的API,其中包括数字签名的实现。数字签名是一种用于验证数据完整性和身份验证的技术,它使用公钥密码学验证数字签名的真实性。 要使用CryptoAPI实现数字签名,可以按照以下步骤进行: 1. 创建一个Cryptographic Service Provider(CSP)对象,用于执行加密和解密操作。 2. 创建一个密钥容器,用于存储数字证书。 3. 获取数字证书并将其存储在密钥容器中。 4. 使用密钥容器中的私钥创建数字签名。 5. 验证数字签名的有效性。 以下是一个示例代码,用于使用CryptoAPI实现数字签名: ``` #include <windows.h> #include <wincrypt.h> #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) int main() { HCRYPTPROV hProv = 0; HCRYPTKEY hKey = 0; HCRYPTHASH hHash = 0; DWORD dwSigLen = 0; BYTE *pbSig = NULL; // Acquire a cryptographic context. if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) { printf("Error: CryptAcquireContext failed\n"); return 1; } // Create a hash object. if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) { printf("Error: CryptCreateHash failed\n"); return 1; } // Hash the data. BYTE pbData[] = "Hello, world!"; DWORD dwDataLen = strlen(pbData); if (!CryptHashData(hHash, pbData, dwDataLen, 0)) { printf("Error: CryptHashData failed\n"); return 1; } // Create a signature of the hash. if (!CryptGetUserKey(hProv, AT_SIGNATURE, &hKey)) { printf("Error: CryptGetUserKey failed\n"); return 1; } if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSigLen)) { printf("Error: CryptSignHash failed (1)\n"); return 1; } pbSig = (BYTE*)malloc(dwSigLen); if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSig, &dwSigLen)) { printf("Error: CryptSignHash failed (2)\n"); return 1; } // Verify the signature. if (!CryptVerifySignature(hHash, pbSig, dwSigLen, hKey, NULL, 0)) { printf("Error: CryptVerifySignature failed\n"); return 1; } printf("Signature verified successfully.\n"); // Clean up. CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); return 0; } ``` 此代码使用SHA1算法对数据进行哈希,并使用AT_SIGNATURE标识符获取密钥容器中的私钥来创建数字签名。然后,它验证数字签名的有效性。在实际情况下,您需要从数字证书中获取公钥并使用它来验证数字签名
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_45303938

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值