为啥 CertificateRequest 的 PublicKey.Key 与私钥不在同一个提供者中?

Posted

技术标签:

【中文标题】为啥 CertificateRequest 的 PublicKey.Key 与私钥不在同一个提供者中?【英文标题】:Why is PublicKey.Key of CertificateRequest isn't in the same provider as the private key?为什么 CertificateRequest 的 PublicKey.Key 与私钥不在同一个提供者中? 【发布时间】:2020-10-25 12:38:35 【问题描述】:

我使用这个创建一个证书请求:

CertificateRequest req = new CertificateRequest(
                          "cn=test",
                          Key,
                          HashAlgorithmName.SHA256,
                          RSASignaturePadding.Pkcs1);

key 是从 CSP 设置的:

ProviderName:“Utimaco CryptoServer CSP”

KeyContainerName: "默认容器"

但在调试时我发现req.PublicKey.Key.CspKeyContainerInfo 指的是不同的提供者:

ProviderName:“Microsoft 增强的 RSA 和 AES 加密提供程序”

KeyContainerName:空

当我尝试获取Exportable 值时:

var isExportable = ((RSACryptoServiceProvider)req.PublicKey.Key).CspKeyContainerInfo.Exportable;

它抛出一个CryptographicException:密钥不存在

我不明白为什么私钥和公钥分别存储在不同的提供者中?! 还是这意味着别的什么?

【问题讨论】:

【参考方案1】:

TL;DR 提供程序设置为通用 RSA 提供程序,但请注意 KeyContainerNamenull。这只是因为它只是在内存中生成,并没有存储在任何地方。

证书签名请求 (CSR) 是在应用程序中创建的公共文件。此时与私钥的“唯一”关系是:

    需要包含公钥; 需要用私钥签名(以表明您在创建 CSR 时有权访问它,并保护它不被更改);

为此,它从密钥对加载公钥,从中创建未签名的 CSR,然后使用私钥对其进行签名。 除此之外,CSR 仅用于请求实际证书。 无需将 CSR/私钥存储在 HSM 中。 这反映在(例如)PKCS#11 根本没有任何 CSR 的概念。

请注意,丢失 CSR 的风险很小,您可以随时重新创建一个。此外,如果有人在您将 CSR 发送到 CA 之前到达 CSR,那么它将被签名验证捕获(或者您会使用错误的公钥获得无效证书,请始终确保已验证并发送尽快提交 CSR)。

当您收到实际证书时,将其存储在 HSM 中是有意义的。通常存储到根的整个证书链,因为协议中通常需要中间证书(包括例如 TLS 和 CMS)。毕竟,它们用于每个签名创建 - 希望您不会将私有对用于您创建它的 PKI 之外的任何其他东西。


许多 HSM 确实提供了存储通用数据的规定。尽管您可以在其中放置 CSR,但它只是一个通用存储;密钥存储本身不会将 CSR 与私钥链接起来,因此您不妨将其保存在存储中,希望在某个受保护的位置,只有您的密钥管理器具有写入权限。

【讨论】:

好点,但这对 API 的设计有何影响,即公钥的 Exportable 属性会引发“密钥不存在”异常? 我的意思是,如果这是一种自然行为(将公钥存储在内存中),为什么不直接返回 false,因为抛出异常可以防止例如序列化它。 我不知道,但“可导出”与否肯定也是一个安全属性,即如果它是可导出的,那么 HSM 可能实际上并没有保护密钥。所以大概它被设置为“true”只是为了表明它们不受保护。但是,由于它不在任何密钥库中,因此您仍然会遇到异常。但这是对这种情况的推理,我必须承认。

以上是关于为啥 CertificateRequest 的 PublicKey.Key 与私钥不在同一个提供者中?的主要内容,如果未能解决你的问题,请参考以下文章

CertManager Letsencrypt CertificateRequest “未能执行自检 GET 请求”

如何将 CSR 文本文件转换为 .NET Core/Standard CertificateRequest 以进行签名?

为啥 ++(*p) 会改变指针值?

为啥 p[:] 在这两种情况下的工作方式不同?

为啥 P 标签不占用行的整个高度?

为啥 ++(*p) 没有给出 l-value required 错误?