18

我在本地机器的根证书存储中插入带有私钥的新 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 

它很可爱....但它是错误的!(2只笨狗参考)

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

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

我只是想知道为什么?

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

我完蛋了!

当我将它编译到 v3.5 时它可以工作!!!!

该怎么办?

4

6 回答 6

11

我遇到了完全相同的问题,结果证明解决方案非常简单。我所要做的就是通过

X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet

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

并像这样安排私钥:

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

var rsaProvider = new RSACryptoServiceProvider(cspParams);

我希望这有帮助。

于 2011-06-23T14:20:20.160 回答
10

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

此外,我发现将私钥保存在UseMachineKeyStore. 在大多数情况下,您需要在某些用户的 My store 中导入带有私钥的证书,并在没有私钥的情况下仅导入 Root 证书。

如果您确实需要将私钥保存在机器密钥存储中,那么您至少应该保护密钥以供某些选定用户读取,而不是从所有人读取。密钥容器只是文件系统中的一个文件(参见目录“%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)导出带有或不带有私钥的证书。在上面的示例中,对于某些特殊的 EKU,我没有限制 CA,因此您可以不受任何限制地使用它,但如果您确实需要限制,您可以在MakeCert.exe中添加其他参数。您还可以使用 MakeCert.exe 创建其他使用 CA 证书签名的证书。因此,您只能针对 MakeCert.exe 制作小型 PKI。

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

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

  • 您应该将其导入组织的每台(Root或多台)计算机中,但您应该导入不带私钥的证书。您可以在以下方面做到这一点AuthRootlocalMachine

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

  • 您应该在一台计算机上导入带有私钥的 CA 证书,并且仅适用于将颁发其他证书的用户(他们将使用 CA 的私钥签署新证书)。一种用于在CurrentUser的My certificate store 中导入证书的用途。所以计算机上的代码可能看起来像

下列的:

// 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 只知道将保存私钥(确切地说是密钥对)的密钥容器,以及将保存证书的 CSP 和证书存储(请参阅http://msdn.microsoft.com/en-us/library/bb204781 .aspx关于商店的位置)。为了能够在证书存储中保存有关密钥容器的信息,Microsoft 引入了所谓的Certificate Extended Properties。如果您在 .NET 中使用X509Certificate2like Thumbprint, FriendlyName, HasPrivateKey,的属性Archived依此类推,您使用证书的扩展属性。所以我建议你导入两次 CA 证书。一次设置RootAuthRoot 设置CERT_KEY_PROV_INFO_PROP_ID 证书扩展属性,一次My存储使用私钥 ( ) 设置有关密钥容器位置的信息CERT_KEY_PROV_INFO_PROP_ID。此外,您可以考虑在使用后直接删除私钥,仅在确实需要使用时才导入,而不是永久保留。所有这些对于拥有更好的安全性都很重要。

于 2010-09-05T11:15:38.060 回答
2

我遇到了这个问题,似乎即使您运行 FindPrivateKey 工具的用户也无权访问密钥,因此您会收到“无法获取私钥文件名”消息。您可以将该工具作为 LocalSystem 进程运行。

更多信息在这里:

http://www.itsolutionbraindumps.com/2011/02/finding-private-key-for-your.html

丁科

于 2011-02-11T16:45:05.883 回答
0

新的 X509Certificate2(localPFXPath, inputPass, X509KeyStorageFlags.MachineKeySet & X509KeyStorageFlags.PersistKeySet); 用 & 代替 | 为我工作。

于 2016-03-10T20:30:59.943 回答
0

通常 Root 中的证书没有要管理的私钥。如果您在 Web 请求中关联密钥,您应该导入到我的文件夹。我有 TLS/SSl 异常,我有客户端证书链。如果您将所有证书链存储在我的商店中,那么我就摆脱了该异常。问题出在用户帐户上。存储证书的实用程序使用当前用户帐户,实际应用程序在系统帐户上运行。

于 2016-08-22T16:29:04.013 回答
0

基本问题是 .NET 证书 API 只是 C++ advapi32 证书管理器 api 的包装器,因此您无需指定传递给该 api 的所有选项,该 api 实际上负责将证书粘贴到 Windows证书存储和持久化密钥。底线是“<em>UseMachineStore”选项需要传递给CspProviderFlags,而 CspProviderFlags 又传递给CAPI.CRYPT_MACHINE_KEYSET。这是决定密钥是否真实持久的小家伙。即使您设置了X509KeyStorageFlags.PersistKeySetMachineKeySetExportable,似乎也有几个不同的原因导致此选项未设置. 只要愚蠢的密钥保留在 C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\ 文件夹中,所有这些选项就有效。如果 CRYPT_MACHINE_KEYSET 在导入时没有设置,那么一旦证书句柄被 GC 处理,advapi32 就会将密钥吹走。

解决方案:在将证书导入个人计算机存储之前,将证书添加到受信任的根。在从 CAPI2 读取日志时,我实际上看到每次“导入”证书时对“X509 对象”的两次调用。一个总是有<Flags value="20" CRYPT_MACHINE_KEYSET="true"/>,(我们想要的),但另一个没有,除非“验证链策略”不返回错误。所以看起来advapi32正在检查证书的“有效性”,或者返回一个被 X509Certificate2 吞噬的异常(我喜欢他们在该代码中有多少个空的 catch 块) 或 advapi32 只是单方面决定不保留不受信任的证书的密钥。(顺便说一句,我怀疑这是 2008 年和 20012 年之间的行为变化,但我还没有证明这一点。)为了解决这个问题,我在我的代码中添加了一个 If-check 以添加证书,如果颁发者等于主题(它是一个自签名证书)然后将证书添加到根,然后再将其添加到 My.

    if (certificate.Issuer.Equals(certificate.Subject))
    {
        using (X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) { 
            store.Open(OpenFlags.ReadWrite);
            store.Add(certificate);
            store.Close();
        }
    }

    using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine)){ 
        store.Open(OpenFlags.ReadWrite);
        store.Add(certificate);
        store.Close();
    }

注意:我发现如果使用没有主题密钥标识符的证书,这是不必要的。不知何故,当您触发 api 实际生成 SKI 而不是将其提交时,它会触发条件将魔术 CRYPT_MACHINE_KEYSET 标志传递给 advapi32。

于 2017-10-26T17:17:07.153 回答