我正在尝试使用CryptoApi在Windows上生成数字签名(来自XP SP3,但目前使用Windows 7进行测试),这将与以下OpenSSL命令兼容:

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

我想使用Windows“我的”钥匙室的私钥进行签名。

我通过使用以下cryptoapi函数(省略简洁的参数),使用SHA1 Digest算法签名文件:

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,该cryptsignhash失败,错误80090016(NTE_BAD_KEYSET)。我知道Keyset确实存在并且可以正常工作,因为我能够使用它来签署SHA1摘要。

我尝试通过从CERTFINDCERTIFIFIFIFIFICATESTORE获得的证书上下文中的信息再次获取上下文:我能做的最好的是成功的CryptGetUserkey呼叫,但是CryptSignHash总是会因同样的错误而失败。

我试图使用的私钥长度为2048位,但是我不希望它与SHA1 Digest一起使用,这不会成为问题。我不知所措,所以任何建议都非常欢迎!

有帮助吗?

解决方案

问题最有可能是在Windows上“知道”的证书存储在哪个提供商中。当您导入证书时,它将将密钥放入某个提供商类型(可能是prop_rsa_full)中,然后稍后尝试通过证书访问键时,它可能最终会以同一提供商类型为单位。

您可能需要打开证书的关联上下文(请查看 pertgetCertificatecontextproperty 使用CERT_KEY_PROV_HANDLE_PROP_ID选项)。使用该处理方法,您可以尝试从原始提供商的上下文中导出密钥,并重新集成到一个新的prop_rsa_aes One(假设键是可以导出的)。

其他提示

80090008 是由于基础提供商而不支持SHA256,SHA384和SHA512引起的,您必须使用 CryptAcquireContext(hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);

以前的大多数答案都有真正答案的一部分。这样做的方法是通过调用密钥容器和“ Microsoft增强RSA和AES加密提供商”的HCRyptProv。

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

所得的HCryptProv可用于签署所有受支持的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).

即使私钥无法导出,此技术也可以工作。

找到证书后,请尝试致电 CertGetCertificateContextPropertyCERT_KEY_PROV_INFO_PROP_ID 用密钥获取提供商和容器的名称。尝试打电话 CryptAcquireContext 使用这些名称,但指定 PROV_RSA_AES 作为提供商类型。

您还可以尝试用“ Microsoft Enharced RSA和AES密码提供商”替换提供商名称,但是除非提供商是其他Microsoft提供商之一,否则这绝对不会起作用。

在发现这篇文章之前,我最近遇到了类似的问题。尽管这篇文章有助于理解问题,但并没有提供解决方案。最后,在Google的帮助下,我解决了这个问题。尽管这个问题是在5年前提出的,但我在这里写了我的解决方案,以防其他人对任何其他人有所帮助。

首先,让我描述我的问题:我们已将OpenSL 0.9.8移植到我们的设备上,以充当SSL Server,而我们还提供了一个在Microsoft Windows上运行的程序作为SSL客户端,该程序还使用OpenSSL库。此客户/服务器模型支持客户端证书身份验证。客户端调用Microsoft Crypto-API在OpenSSL中实现RSA_Method以支持证书身份验证。到目前为止,在我们将OpenSSL库从0.9.8升级到1.0.2之前,都很好 TLSV1.2. 。选择TLSV1.2,客户端证书身份验证始终失败。调试后,我发现问题是在客户端程序的RSA_SIGN方法中。此方法签名哈希结果以获取将在SSL客户端验证消息中使用的数字签名。签署操作是使用 CertFindCertificatePrivateKey/CryptCreateHash/CryptSetHashParam/CryptSignHash 如这个问题所述。该错误发生在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证书文件,如网页所述;重新提出证书。然后解决了问题。

希望这很有帮助。 :-)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top