Windows CryptoAPI:带有 CALG_SHA_256 的 CryptSignHash 和来自我的密钥库的私钥

发布于 2024-10-02 19:56:54 字数 1480 浏览 8 评论 0原文

我正在尝试使用 CryptoAPI 在 Windows(从 XP SP3 开始,但当前使用 Windows 7 进行测试)上生成数字签名,该签名将与以下 openssl 命令兼容:

openssl dgst -sha256 -sign <parameters> (for signing)
openssl dgst -sha256 -verify <parameters> (for validation)

我想使用 Windows“MY”密钥库中的私钥进行签名。

我设法使用以下 CryptoAPI 函数(为了简洁而省略参数)使用 SHA1 摘要算法对文件进行签名:

CertOpenStore
CertFindCertificateInStore
CryptAcquireCertificatePrivateKey
CryptCreateHash (with CALG_SHA1)
CryptHashData
CryptSignHash

生成的签名与“openssl dgst -sha1 -verify”兼容(一旦反转字节顺序)。

我的问题是:当我尝试将 CALG_SHA_256 与 CryptCreateHash 一起使用时,它失败并出现错误 80090008 (NTE_BAD_ALGID)。通过谷歌搜索周围,我发现我需要使用特定的提供程序(PROV_RSA_AES)而不是默认的提供程序。由于我将拥有提供程序句柄,因此我还需要将 CryptAcquireCertificatePrivateKey 替换为 CryptGetUserKey。所以我修改了我的程序,如下所示:

CryptAcquireContext (with PROV_RSA_AES)
CertOpenStore
CertFindCertificateInStore
CryptGetUserKey
CryptCreateHash (with CALG_SHA256)
CryptHashData
CryptSignHash

不幸的是,这没有按预期工作:CryptGetUserKey 失败,错误为 8009000D (NTE_NO_KEY)。如果我删除 CryptGetUserKey 调用,程序将运行直到 CryptSignHash,该操作失败并出现错误 80090016 (NTE_BAD_KEYSET)。我知道密钥集确实存在并且工作正常,因为我能够使用它来签署 SHA1 摘要。

我尝试使用从 CertFindCertificateInStore 获得的证书上下文中的信息再次获取上下文:我能做的最好的事情就是成功调用 CryptGetUserKey,但 CryptSignHash 总是会因相同的错误而失败。

我尝试使用的私钥长度为 2048 位,但我不认为它会成为问题,因为它适用于 SHA1 摘要。我很茫然,所以非常欢迎任何建议!

I am trying to generate digital signatures on Windows (from XP SP3, but currently testing with Windows 7) with CryptoAPI that will be compatible with the following openssl commands:

openssl dgst -sha256 -sign <parameters> (for signing)
openssl dgst -sha256 -verify <parameters> (for validation)

I want to use a private key from the Windows "MY" keystore for signing.

I managed to sign files using the SHA1 digest algorithm by using the following CryptoAPI functions (omitting parameters for brevity):

CertOpenStore
CertFindCertificateInStore
CryptAcquireCertificatePrivateKey
CryptCreateHash (with CALG_SHA1)
CryptHashData
CryptSignHash

The generated signature is compatible with "openssl dgst -sha1 -verify" (once the byte order is reversed).

My problem is: when I try to use CALG_SHA_256 with CryptCreateHash, it fails with error 80090008 (NTE_BAD_ALGID). By googling around, I found that I needed to use a specific provider (PROV_RSA_AES) instead of the default one. Since I would have a provider handle, I would also need to replace CryptAcquireCertificatePrivateKey by CryptGetUserKey. So I modified my program to look like:

CryptAcquireContext (with PROV_RSA_AES)
CertOpenStore
CertFindCertificateInStore
CryptGetUserKey
CryptCreateHash (with CALG_SHA256)
CryptHashData
CryptSignHash

Unfortunately, this didn't work as expected: CryptGetUserKey failed with error 8009000D (NTE_NO_KEY). If I remove the CryptGetUserKey call, the program runs until CryptSignHash, which fails with error 80090016 (NTE_BAD_KEYSET). I know the keyset does exist and works fine, since I was able to use it to sign the SHA1 digest.

I tried acquiring the context again with information from the certificate context I got from CertFindCertificateInStore: the best I could do was a successful CryptGetUserKey call, but CryptSignHash would always fail with the same error.

The private key I am trying to use is 2048 bits long, but I don't expect it to be a problem since it works with the SHA1 digest. I am at a loss, so any suggestion would be very welcome!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

北风几吹夏 2024-10-09 19:56:54

80090008 是因为 Base 提供程序不支持 SHA256、SHA384 和 SHA512,您必须使用 CryptAcquireContext(hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);

80090008 is caused because of Base provider does not support SHA256, SHA384 and SHA512, you got to use CryptAcquireContext(hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);

青衫负雪 2024-10-09 19:56:54

之前的大部分答案都包含了真实答案的一部分。执行此操作的方法是通过调用获取使用密钥容器和“Microsoft 增强型 RSA 和 AES 加密提供程序”的 HCRYPTPROV

CryptAcquireContext(&hCryptProv, <keyContainerName>, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_SILENT)

生成的 hCryptProv 可用于对使用所有受支持的 SHA2 创建的哈希值进行签名:256、384、512。

如果密钥是通过证书获取的,则可以使用带有参数 CERT_KEY_PROV_INFO_PROP_ID 的 CertGetCertificateContextProperty() 来获取密钥容器名称,如果该密钥已经有 HCRPYPTPROV(例如,使用 CryptAcquireCertificatePrivateKey 获取),则可以通过带有参数 PP_CONTAINER 的 CryptGetProvParam() 来获取密钥容器名称。

即使私钥不可导出,此技术也有效。

Most of the previous answers have parts of the real answer. The way to do this is to get a HCRYPTPROV that uses the key container and the "Microsoft Enhanced RSA and AES Cryptographic Provider" by calling

CryptAcquireContext(&hCryptProv, <keyContainerName>, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_SILENT)

The resulting hCryptProv can be used to sign hashes created with all the supported SHA2: 256, 384, 512.

The Key Container Name can be obtained either with CertGetCertificateContextProperty() with argument CERT_KEY_PROV_INFO_PROP_ID if the key is obtained through the certificate, or by CryptGetProvParam() with argument PP_CONTAINER if there's already a HCRPYPTPROV for that key ( for example, obtained with CryptAcquireCertificatePrivateKey).

This technique works even if the private key is not exportable.

恋竹姑娘 2024-10-09 19:56:54

问题很可能是 Windows 上的证书“知道”其私钥存储在哪个提供商中。当您导入证书时,它会将密钥放入特定的提供程序类型(可能是 PROV_RSA_FULL),当您稍后尝试通过证书访问密钥时,它可能最终会处于相同的提供程序类型中。

您可能需要打开证书的关联上下文(查看 CertGetCertificateContextProperty 与 CERT_KEY_PROV_HANDLE_PROP_ID 选项)。
使用该句柄,您可以尝试从原始提供者上下文中导出密钥,然后重新导入到新的 PROV_RSA_AES 中(假设密钥是可导出的)。

The problem is most likely to be that certificates on Windows "know" in which provider their private keys are stored. When you import your cert it would put the key into a certain provider type (probably PROV_RSA_FULL), when you then later try to access the key via the certificate it will probably end up in the same provider type.

You probably need to open the associated context for the certificate (have a look at CertGetCertificateContextProperty with the CERT_KEY_PROV_HANDLE_PROP_ID option).
With that handle yyou could try exporting the key from the original provider context and reimporting into a new PROV_RSA_AES one (assuming the key is exportable).

洛阳烟雨空心柳 2024-10-09 19:56:54

找到证书后,尝试使用 CERT_KEY_PROV_INFO_PROP_ID 调用 CertGetCertificateContextProperty 来获取提供程序和包含密钥的容器的名称。
尝试使用这些名称调用 CryptAcquireContext,但指定 PROV_RSA_AES 作为提供程序类型。

您还可以尝试将提供程序名称替换为“Microsoft 增强型 RSA 和 AES 加密提供程序”,但这肯定行不通,除非该提供程序是其他 Microsoft 提供程序之一。

Once you have found the certificate, try calling CertGetCertificateContextProperty with CERT_KEY_PROV_INFO_PROP_ID to get the name of the provider and container with the key.
Try calling CryptAcquireContext with these names, but specifying PROV_RSA_AES as the provider type.

You might also try replacing the provider name with "Microsoft Enhanced RSA and AES Cryptographic Provider", but this will definitely not work unless the provider is one of the other Microsoft providers.

寄人书 2024-10-09 19:56:54

在找到这篇文章之前,我最近遇到了类似的问题。虽然这篇文章有助于理解问题,但它没有给出解决方案。最后在Google的帮助下,我解决了这个问题。虽然这个问题是 5 年前提出的,但我在这里写下我的解决方案,以防它对其他人有所帮助。

首先,让我描述一下我的问题:我们在我们的设备上移植了 OpenSSL 0.9.8 作为 SSL 服务器,同时我们还提供了一个在 Microsoft Windows 上运行的程序作为 SSL 客户端,该程序也使用 OpenSSL 库。此客户端/服务器模型支持客户端证书身份验证。客户端调用OpenSSL中实现RSA_method的Microsoft Crypto-API来支持证书身份验证。到目前为止,在我们将 OpenSSL 库从 0.9.8 升级到 1.0.2 以支持 TLSv1.2 之前一切顺利。选择 TLSv1.2 后,客户端证书身份验证始终失败。经过调试,发现问题出在客户端程序的RSA_sign方法上。此方法对哈希结果进行签名以获得将在 SSL 客户端验证消息中使用的数字签名。签名操作是使用 CertFindCertificatePrivateKey/CryptCreateHash/CryptSetHashParam/CryptSignHash API 计算的,如本问题所述。错误发生在 CryptCreateHash 调用中,其中请求的哈希方法是 SHA-384,但 CertFindCertificatePrivateKey 检索到的 hCryptoProv 对应于 CSP(Microsoft 增强型加密提供程序 v1.0)不支持 SHA-2 哈希方法。

这是一个真正乏味的描述。如果您仍在继续阅读,我想您也遇到过类似的问题。让我向您介绍我的解决方案。

我的第一反应是调整 SSL 协商参数,将哈希算法降级为 SHA-1,但失败了。看来 SHA-2 哈希对于 TLSv1.2 是强制性的。我也尝试过通过证书私钥加密来实现签名操作,也失败了。 Crypto-API没有提供证书私钥加密的接口(我不确定这一点,但我没有找到它。)经过两天无果的尝试,我找到了这个网页: http://www.componentspace.com/Forums/Topic1578.aspx。这真的就像隧道尽头的光明。该证书与不支持 SHA-2 的 CSP 绑定,如果我们可以将其转换为支持 SHA-2 的 CSP,一切都会好起来的。如果链接损坏,我将转换方法粘贴在这里:

openssl pkcs12 -export -in idp.pem -out new-idp.pfx -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider"

我刚刚按照网页所述重建了 .pfx 证书文件;重新导入证书。那么问题就解决了。

希望这有帮助。 :-)

I encountered a similar problem recently before I found this post. Although this post is helpful in comprehension of the problem, it didn't give out a solution. Finally, with the help of Google, I solved the problem. Although this question was asked 5 years ago, I write my solution here in case it will help any other guy.

First, let me describe my problem: We've ported OpenSSL 0.9.8 on our device acting as a SSL server, while we also provided a program running on Microsoft Windows as a SSL client, which also using OpenSSL library. This client/server model supporting client certificate authentication. The client call the Microsoft Crypto-API implementing RSA_method in OpenSSL to support the certificate authentication. So far, so good before we upgrading the OpenSSL library from 0.9.8 to 1.0.2 to support TLSv1.2. With TLSv1.2 chosen, the client certificate authentication always failed. After debugging, I found the problem is in the RSA_sign method of the client program. This method sign the hash result to get the digital signature which will be used in the SSL Client Verify message. The signing operation is calculated using CertFindCertificatePrivateKey/CryptCreateHash/CryptSetHashParam/CryptSignHash APIs as this question described. The error occurred in the CryptCreateHash call, where the requested hash method is SHA-384 but the hCryptoProv retrieved by CertFindCertificatePrivateKey corresponded to a CSP (Microsoft Enhanced Cryptographic Provider v1.0) does NOT support SHA-2 hash methods.

This is a real tedious description. If you are still continue reading, I guess you’ve met a similar problem too. Let me introduce you my solution.

My first reaction was to adjust the SSL negotiation param to downgrade the hash algorithm to SHA-1, but I failed. It seems SHA-2 hash is obligatory for TLSv1.2. Also did I try to achieve the sign operation by encrypting with the certificate private key, which was also failed. Crypto-API provides NO interface for certificate private key encryption (I’m not sure of this, but I did not found it.) After two days of resultless trying, I found this webpage: http://www.componentspace.com/Forums/Topic1578.aspx. It’s really like the light at the end of the tunnel. The certificate is binding with a CSP not supporting SHA-2, if we can convert it to a CSP supporting SHA-2, everything will be OK. In case of broken link, I paste the converting method here:

openssl pkcs12 -export -in idp.pem -out new-idp.pfx -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider"

I just rebuilt the .pfx certificate file as the webpage said; re-imported the certificate. Then the problem is solved.

Hope this is helpful. :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文