Windows CryptoAPI:带有CALG_SHA_256的CryptSignHash和来自MY keystore的私钥

Dom*_*Eav 3 cryptography rsa sha256 cryptoapi digital-signature

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

openssl dgst -sha256 -sign <parameters> (for signing)
openssl dgst -sha256 -verify <parameters> (for validation)
Run Code Online (Sandbox Code Playgroud)

我想使用Windows"MY"密钥库中的私钥进行签名.

我设法使用SHA1摘要算法使用以下CryptoAPI函数签署文件(为简洁省略参数):

CertOpenStore
CertFindCertificateInStore
CryptAcquireCertificatePrivateKey
CryptCreateHash (with CALG_SHA1)
CryptHashData
CryptSignHash
Run Code Online (Sandbox Code Playgroud)

生成的签名与"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
Run Code Online (Sandbox Code Playgroud)

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

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

我试图使用的私钥是2048位长,但我不认为它是一个问题,因为它与SHA1摘要一起使用.我很茫然,所以任何建议都会非常受欢迎!

小智 9

80090008 是由于Base Provider不支持SHA256,SHA384和SHA512引起的,你必须使用 CryptAcquireContext(hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);


小智 6

以前的大多数答案都包含真实答案的部分内容。这样做的方法是通过调用获取使用密钥容器和“Microsoft Enhanced RSA and AES Cryptographic Provider”的HCRYPTPROV

CryptAcquireContext(&hCryptProv, <keyContainerName>, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_SILENT)
Run Code Online (Sandbox Code Playgroud)

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

密钥容器名称可以通过 CertGetCertificateContextProperty() 获得,参数 CERT_KEY_PROV_INFO_PROP_ID 如果密钥是通过证书获得的,或者通过 CryptGetProvParam() 与参数 PP_CONTAINER 获得,如果该密钥已经有一个 HCRPYPTPROV(例如,通过 CryptAcquireCertificateCquire 获得)。

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