在我看来,您应该以其他方式导入密钥。示例见http://support.microsoft.com/kb/950090。
此外,我发现将私钥保存在UseMachineKeyStore 中并不好。在大多数情况下,您需要在某些用户的 My store 中导入带有私钥的证书,并在没有私钥的情况下仅导入 Root 证书。
如果您确实需要将私钥保存在机器密钥存储中,那么您至少应该保护密钥以供某些选定用户读取,而不是从所有人读取。密钥容器只是文件系统中的一个文件(参见目录“%ALLUSERSPROFILE%\Microsoft\Crypto\Keys”中的文件),它与 NTFS 中的其他文件一样具有安全描述符。要更改文件的安全描述符,您可以使用CspKeyContainerInfo.CryptoKeySecurity 属性和AddAccessRule、RemoveAccessRule 等。
已更新:首先对冗长的回答表示抱歉。
我可以将your program code 分成两部分。在第一部分中,您生成一个可用作 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)。在上面的示例中,对于某些特殊的 EKU,我没有限制 CA,因此您可以不受限制地使用它,但如果您确实需要限制,您可以在 MakeCert.exe 中添加额外的参数。您还可以使用 MakeCert.exe 创建其他使用 CA 证书签名的证书。因此,您只能针对 MakeCert.exe 制作小型 PKI。
在我看来,创建证书实际上是代码的一个单独部分。你的主要问题在第二部分。
如果你想导入 CA 证书,你应该考虑一些重要的事情:
- 您应该在您组织的每台(或多台)计算机上将其导入
Root 或AuthRoot 中的localMachine,但您应该不带私钥导入证书。您可以在以下方面做到这一点
CertMgr.exe -add -c CA.cer -s -r localMachine AuthRoot
- 您应该在一台计算机上的计算机上使用私钥导入 CA 证书,并且仅适用于将颁发其他证书的用户(谁将使用 CA 的私钥签署新证书)。一种用于在 CurrentUser 的 My 证书存储中导入证书。所以计算机上的代码可能看起来像
以下:
// 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.My 和StoreLocation.CurrentUser 更改为其他值,代码将起作用,但我不建议您这样做。
一般来说,在 .NET 代码中导入证书看起来有点奇怪,并且没有显示在后台会做什么。 Windows 只知道Key Containers 将保存关于 CSP 的私钥(确切地说是密钥对)和Certificate Stores 将保存证书的位置(请参阅http://msdn.microsoft.com/en-us/library/bb204781.aspx 关于存储的位置)。为了能够将有关密钥容器的信息保存在 Microsoft 引入的名为 Certificate Extended Properties 的证书存储中。如果您在 .NET 中使用 X509Certificate2 的属性,例如 Thumbprint、FriendlyName、HasPrivateKey、Archived 等,则可以使用证书的扩展属性。所以我建议你导入两次 CA 证书。一次在Root 或AuthRoot 没有设置CERT_KEY_PROV_INFO_PROP_IDCertificate Extended Properties 一次在My 存储with关于Key所在位置的信息的设置带有私钥 (CERT_KEY_PROV_INFO_PROP_ID) 的容器。此外,您可以考虑在使用后直接删除私钥,仅在确实需要使用且不要永久保留时才导入。所有这些对于提高安全性都很重要。