从证书存储导入的X509证书没有私钥。

5

以下是使用 .Net Core 2.2 将带有私钥的证书导入到 Windows 证书存储中的简单代码:

  using (var store = new X509Store(StoreName.Root,StoreLocation.CurrentUser))
  {
      store.Open(OpenFlags.ReadWrite);
      store.Add(cert);
      store.Close();
  }

以下是一个同样简单的代码,用于将其读取出来:

 using (var store = new X509Store(StoreName.Root,StoreLocation.CurrentUser))
 {
    store.Open(OpenFlags.ReadOnly);
    var certCollection = store.Certificates.Find(X509FindType.FindBySubjectName, commonName, validOnly);
    store.Close();
    return certCollection;
 }

然而,尽管证书被成功检索到certCollection中,但它的私钥为null且hasPrivateKey为false,即使在之前的Add调用中它们不是null和true。这是为什么?
更新:
using (RSA rsa = RSA.Create(keySize)) {    
     CertificateRequest certRequest = new CertificateRequest(
         subjectName,
         rsa,
         HashAlgorithmName.SHA512,
         RSASignaturePadding.Pkcs1);

     certRequest.CertificateExtensions
         .Add(newX509SubjectKeyIdentifierExtension(certRequest.PublicKey, false));  
     return certRequest;
}

Windows不希望根存储中的证书有关联的私钥(如果有,将在“我的”存储中有一个知道私钥的副本)。可能是Windows在保存到存储中时只擦除了关联密钥的信息。 - bartonjs
1
在第一个示例中,能够看到 cert 来自哪里会很好。 - Crypt32
@Crypt32 Cert只是一个在内存中使用C#创建的X509Certificate2。 - rism
@bartonjs,我的商店中的副本也没有私钥,如果我们只想使用有效证书,由于将CA证书放入我的商店中,那么后续的My store读取将失败。 - rism
使用C#创建的 - 是如何创建的?从具有EphemeralKeySet标志的PFX文件中打开的?使用CertificateRequest吗?(如果是后者,则您可能没有创建持久的密钥,且短暂密钥不会在存储区添加时持续存在) - bartonjs
@bartonjs 你说得对,我确实这样做了。我没有意识到设置keySize会使它成为短暂的。话虽如此,如果我使用文档建议的非短暂RSA.Create(),结果也不会有任何不同。 - rism
2个回答

6

您的密钥被创建为临时密钥,因此当它被添加到持久存储中时,该密钥将被丢弃。

如果您想将密钥保留到存储证书中,您需要直接将其创建为持久密钥,或者导出为PFX文件并重新导入(这是最简单的方法):

// If you're planning on saving to a LocalMachine store you should also | in the
// X509KeyStorageFlags.MachineKeySet bit.
X509KeyStorageFlags storageFlags = X509KeyStorageFlags.PersistKeySet;

X509Certificate2 certWithPersistedKey =
    new X509Certificate2(
        certWithEphemeralKey.Export(X509ContentType.Pkcs12, ""),
        "",
        storageFlags);

现在,可以像您期望的那样添加certWithPersistedKey

很酷,我特意希望你在我提问时能看到我的问题,因为我读了你在其他SO答案和github上的许多好东西,都非常出色。所以谢谢。这似乎也解决了我创建一个中间CA证书与根CA证书的次要问题。当我尝试使用中间证书签署终端实体证书时,它们会失败,并说根CA没有密钥-需要进行更多测试,但目前为止还不错。 - rism
实际上,对于我的第二个问题,我还需要使用certificate.CopyWithPrivateKey(rsa);,因为当使用发行者证书创建方法时,它不会像创建自签名证书时那样执行此操作。关于CopyWithPrivateKey如何工作的讨论可以在这里找到:https://github.com/dotnet/corefx/issues/19888 - rism

0
X509Certificate2 cert = new X509Certificate2("a.pfx", "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(xCertificate);

原因 当使用X509Certificate或X509Certificate2类安装证书时,默认情况下,X509Certificate或X509Certificate2会创建一个临时容器来导入私钥。当不再引用私钥时,私钥将被删除。

解决方案 为了创建一个永久的密钥容器以存储私钥,必须使用X509KeyStorageFlags.PersistKeySet标志,防止.NET删除密钥容器。应该使用以下代码:

参考:https://learn.microsoft.com/en-us/troubleshoot/developer/dotnet/framework/general/install-pfx-file-using-x509certificate


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接