2

在为 OpenSSL 和客户端证书使用 Windows 密钥存储的过程中,我发现了这篇文章: https ://anexdev.blogspot.com/2018/10/how-to-send-client-certificate-with.html

除了给定代码中的一些错别字之外,该示例启发我编写了一些代码,这些代码利用 ENGINE CAPI 进行密钥使用。总而言之,在商店 ( L"MY") 中找到证书后,我能够发出 ENGINE_load_private_key() 最终返回一个新分配的 EVP_PKEY 结构。

但是我无法将此密钥与证书结合使用。经过一番调查,我发现该结构仅包含一个版本、一个模数和一个 pubkey 组件:

RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d  (this field and everything following is mising)
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

因此,我查看了 OpenSSL 实现的代码。事实上, ENGINE_load_private_key() 至少在 CAPI 引擎明显不打算导出私钥的情况下。

openssl/crypto/engine/eng_pkey.c中,该函数调用load_privkey()所选引擎的成员。对于 CAPI,实现在openssl/engines/e_capi.c中定义。此函数调用capi_find_key()以查找密钥并capi_get_pkey()接收它。

仔细研究static EVP_PKEY *capi_get_pkey(ENGINE *eng, CAPI_KEY *key)似乎可以解释我的问题的根源:

   if (!CryptExportKey(key->key, 0, PUBLICKEYBLOB, 0, pubkey, &len)) {
        CAPIerr(CAPI_F_CAPI_GET_PKEY, CAPI_R_PUBKEY_EXPORT_ERROR);
        capi_addlasterror();
        goto err;
    }

为什么用capi_get_pkey()调用?这是故意规避 Windows 的任何规则并由于已知的 Windows 错误而提取私钥,还是 <code>ENGINE_load_private_key() 实际上与 CAPI 结合使用?CryptExportKey()PUBLICKEYBLOB

有谁知道我的应用程序的 <code>ENGINE_load_private_key() 替代品?

4

1 回答 1

3

我碰到了同一堵墙,做了一个小的研究。看起来这不是错误,而是 Microsoft CryptoAPI 的工作方式。在不可导出密钥的情况下,无法使用 CryptExportKey 和 PRIVATEKEYBLOB 从 Windows 证书存储中导出密钥。因此,您可以只获得一个参考,一个密钥对的 id,稍后您可以使用它通过 CryptoAPI 进行所有加密操作(不能导出真正的密钥)。因此 openssl CAPI 引擎遵循了这条路径,他们只获得一个所需的密钥对 id,然后将其泵入 CryptoAPI 以执行所需的操作。

我认为这也是未实现 capi_rsa_priv_enc 的原因(CryptEncrypt 总是从提供的密钥对 id 中选择公钥)并且因为没有真正的导出私钥(只是密钥对 id),他们甚至无法使用私钥执行加密甚至在 CryptoAPI 旁边。但这只是我的猜测。

于 2021-01-28T08:31:00.287 回答