在Root中插入证书(使用私钥),LocalMachine证书存储在.NET 4中失败怎么办?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (17)

我在使用本地机器的根证书存储区中插入带有私钥的新CA证书时遇到问题。

这是发生的事情:

//This doesn't help either.
new StorePermission (PermissionState.Unrestricted) { Flags = StorePermissionFlags.AddToStore }.Assert();
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
privkey.PersistKeyInCsp = true;
//This shouldn't be necessary doesn't make a difference what so ever.
RSACryptoServiceProvider.UseMachineKeyStore = true;
cert.PrivateKey = privkey;
store.Open (OpenFlags.MaxAllowed);
store.Add (cert);
store.Close ();

证书被插入,它看起来很漂亮:

注:据说它有一个私钥。

所以你会说一个人可以用FindPrivateKey找到它

C:\Users\Administrator\Desktop>FindPrivateKey.exe Root LocalMachine -t "54 11 b1 f4 31 99 19 d3 5a f0 5f 01 95 fc aa 6f 71 12 13 eb"
FindPrivateKey failed for the following reason:
Unable to obtain private key file name

Use /? option for help 

证书导出对话框给了我这个非常好的信息:

此代码在使用此代码段模拟管理员时运行:单击此处

我只想知道为什么?

(在Windows Server 2008 R2和Windows 7上测试)

提问于
用户回答回答于

我有完全相同的问题,并且解决方案变得非常简单。我所要做的就是通过

X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet

到X509Certificate2的ctor。现在,正在使用DotNetUtilities将bouncycastle证书转换为.net one,但该辅助方法使用DefaultKeySet(而不是MachineKeySet + PersistKeySet)创建.net证书。

并像这样安排私钥:

var cspParams = new CspParameters
{
      KeyContainerName = Guid.NewGuid().ToString(),
      KeyNumber = (int)KeyNumber.Exchange,
      Flags = CspProviderFlags.UseMachineKeyStore
};

var rsaProvider = new RSACryptoServiceProvider(cspParams);
用户回答回答于

在我看来,你应该以其他方式导入密钥。有关示例,请参阅http://support.microsoft.com/kb/950090

此外,我发现不好保存私钥UseMachineKeyStore。在大多数情况下,需要使用某个用户的“我的商店”中的私钥导入证书,并在没有私钥的仅根证书中导入。

它确实需要保存机密钥库上的私钥,至少应该保护只读取某些选定用户而不是每个人的密钥。密钥容器只是文件系统中的一个文件(请参阅配置文件“%ALLUSERSPROFILE%\ Microsoft \ Crypto \ Keys”中的文件),它具有与NTFS中其他文件类似的安全描述符。要更改文件的安全描述符,您可以使用CspKeyContainerInfo.CryptoKeySecurity属性AddAccessRuleRemoveAccessRule等等。

我可以将程序代码分为两部分。在第一部分中,将生成一个可用作CA证书的自签名证书,并将其保存为rootcert.pfx文件。在第二部分中,导入证书,但使用RSACryptoServiceProvider填充先前创建的密钥的属性而不是使用rootcert.pfx

我建议将代码的第二部分替换为更加标准和简单的代码:使用rootcert.pfx中的私钥导入证书,如http://support.microsoft.com/kb/950090中所述。它工作得很好。

我不用自己的BouncyCastle,所以我不能评论你的代码的第一部分,但总的来说,你可以在代码中做什么,你也可以在Windows SDK 中使用MakeCert.exe实用程序。你可以像下面这样做

MakeCert.exe -pe -ss MY -a sha1 -cy authority -len 2048 -m 120 -r -# 1
             -n "CN=Some Root CA, C=NL, OU=BleedingEdge, ST=Somewhere, L=Somelane"

然后,可以导出带有或不带私钥的证书,以证书管理单元(适用于mmc.exe)。在上面的示例中,我没有将CA限制为一些特殊的EKU,因此可以毫无限制地使用它,但是如果您确实需要这些限制,则可以将其他参数添加到MakeCert.exe中。还可以使用MakeCert.exe创建其他使用CA证书签名的证书。因此,只能对MakeCert.exe进行小型PKI。

在我看来,创建证书实际上是代码的一个独立部分。你的主要问题在第二部分。

如果你想要导入CA证书,你应该考虑一些重要的事情:

  • 你应该将其导入RootAuthRootlocalMachine组织中的每一个(或多个)的计算机上,但你应该导入证书没有私钥。你可以在跟随方面做到这一点

CertMgr.exe -add -c CA.cer -s -r localMachine AuthRoot

  • 应该在台计算机上的计算机上导入带有私钥的 CA证书并且仅针对将颁发其他证书(将使用CA的私钥签署新证书)的用户导入。一种用于在CurrentUser我的证书存储中导入证书的方法。所以计算机上的代码可能看起来像

以下:

// import PFX
X509Certificate2 cert = new X509Certificate2 (@"c:\Oleg\rootcert.pfx", "password",
    X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// save certificate and private key
X509Store storeMy = new X509Store (StoreName.My, StoreLocation.CurrentUser);
storeMy.Open (OpenFlags.ReadWrite);
storeMy.Add (cert);

// get certificate without private key
// one can import certificate from rootcert.cer instead
byte[] certBlobWithoutPrivateKey = cert.Export (X509ContentType.Cert);
// save pure certificate in Root of the local machine
X509Certificate2 certWithoutPrivateKey = new X509Certificate2 (certBlobWithoutPrivateKey);
X509Store storeRoot = new X509Store (StoreName.Root, StoreLocation.LocalMachine);
storeRoot.Open (OpenFlags.ReadWrite);
storeRoot.Add (certWithoutPrivateKey);

如果的确会改变StoreName.MyStoreLocation.CurrentUser改为其他值,该代码将起作用,但我不建议这样做。

一般来说,在.NET代码中导入证书看起来有点奇怪,并不能说明底层会做什么。Windows只知道密钥容器,其中的私钥(完全是密钥对)将被保存在将保存证书的CSP和证书存储中(请参阅http://msdn.microsoft.com/zh-cn/library/bb204781 .aspx关于商店的位置)。为了能够在证书存储中保存关于密钥容器的信息,Microsoft引入了如此命名的证书扩展属性。如果在.NET属性使用X509Certificate2ThumbprintFriendlyNameHasPrivateKeyArchived等等你使用证书的扩展属性。所以我建议你两次导入CA证书。在一个RootAuthRoot 没有设置CERT_KEY_PROV_INFO_PROP_ID 证书扩展属性并一次My储存信息有关密钥容器的地方使用私钥(设置CERT_KEY_PROV_INFO_PROP_ID)。此外,可以考虑在使用后直接删除私钥,只有在确实需要使用而不是永久保留私钥时才能导入私钥。所有这些对于提高安全性都很重要。

扫码关注云+社区