3

我正在尝试使用与以下 openssl 命令兼容的 CryptoAPI 在 Windows(来自 XP SP3,但目前正在使用 Windows 7 测试)上生成数字签名:

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) 而不是默认的提供程序因为我有一个提供者句柄,所以我还需要用 CryptGetUserKey 替换 CryptAcquireCertificatePrivateKey。所以我修改了我的程序看起来像:

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 摘要。我很茫然,所以任何建议都会非常受欢迎!

4

5 回答 5

9

80090008是因为 Base provider 不支持 SHA256、SHA384 和 SHA512,你必须使用CryptAcquireContext(hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);

于 2012-04-24T09:05:12.933 回答
7

以前的大多数答案都有部分真实答案。执行此操作的方法是通过调用获取使用密钥容器和“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() 获得。

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

于 2015-10-20T23:22:37.817 回答
1

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

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

于 2010-11-16T06:31:11.377 回答
0

找到证书后,尝试调用CertGetCertificateContextPropertywithCERT_KEY_PROV_INFO_PROP_ID以获取提供者的名称和带有密钥的容器。尝试CryptAcquireContext使用这些名称调用,但指定PROV_RSA_AES为提供程序类型。

您也可以尝试将提供程序名称替换为“Microsoft Enhanced RSA and AES Cryptographic Provider”,但除非提供程序是其他 Microsoft 提供程序之一,否则这绝对行不通。

于 2010-11-16T08:43:17.093 回答
0

在找到这篇文章之前,我最近遇到了类似的问题。尽管这篇文章有助于理解问题,但它并没有给出解决方案。最后,在谷歌的帮助下,我解决了这个问题。尽管这个问题是 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 Enhanced Cryptographic Provider v1.0)不支持 SHA-2 哈希方法。

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

我的第一反应是调整 SSL 协商参数以将哈希算法降级为 SHA-1,但我失败了。似乎 SHA-2 哈希对于 TLSv1.2 是强制性的。我也尝试通过使用证书私钥加密来实现签名操作,这也失败了。Crypto-API 没有提供证书私钥加密的接口(我不确定,但我没有找到它。)经过两天的无果尝试,我找到了这个网页: http: //www.componentspace.com/论坛/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 证书文件;重新导入证书。然后问题就解决了。

希望这会有所帮助。:-)

于 2016-09-06T07:01:15.073 回答