在Root中插入证书(带私钥),本地计算机证书存储在.NET 4中失败

时间:2010-09-02 09:07:58

标签: c# .net-4.0 certificate x509certificate bouncycastle

我在localmachine的根证书存储区中插入带有私钥的新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 ();

证书被插入并且看起来都很花哨:(见!)note it says it has a private key

注意:据说它有私钥。

所以你会说用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只愚蠢的狗参考)

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

在使用此代码段模拟管理员时运行此代码:click here

我只是想知道为什么?

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

我会被诅咒!

当我将其编译为v3.5时,它可以工作!!!!

怎么办?

6 个答案:

答案 0 :(得分:9)

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

此外,我发现在UseMachineKeyStore中保存私钥并不好。在大多数情况下,您需要在某个用户的My store中使用私钥导入证书,并在没有私钥的Root only证书中导入。

您需要在计算机密钥存储区中保存私钥,至少应该保护密钥以便仅为某些选定用户而非Everyone进行读取。密钥容器只是文件系统中的一个文件(请参阅diriectory“%ALLUSERSPROFILE%\ Microsoft \ Crypto \ Keys”中的文件),该文件具有安全描述符,如NTFS中的其他文件。要更改文件的安全描述符,您可以使用CspKeyContainerInfo.CryptoKeySecurity属性和AddAccessRuleRemoveAccessRule等。

更新:首先对不起,答应很长。

我可以将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中的AuthRootlocalMachine中导入组织的每台(或多台)计算机,但是您应该导入证书而不使用私钥< / strong>即可。您可以通过以下方式执行此操作

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

  • 您应该在一台计算机上的计算机上导入带有私钥的 ,而仅针对将颁发其他证书的用户(谁将使用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只知道Key Containers其中私钥(确切地说是密钥对)将保存在CSP和Certificate Stores中,其中将保存证书(请参阅http://msdn.microsoft.com/en-us/library/bb204781.aspx关于商店的位置) 。为了能够在Microsoft推出的名为Certificate Extended Properties的证书存储中保存有关密钥容器的信息。如果您使用X509Certificate2的.NET属性,例如ThumbprintFriendlyNameHasPrivateKeyArchived等,则可以使用证书的扩展属性。所以我建议你两次导入CA证书。一个位于RootAuthRoot ,不设置CERT_KEY_PROV_INFO_PROP_ID Certificate Extended Properties,另一个位于My存储 使用私钥(CERT_KEY_PROV_INFO_PROP_ID)设置有关密钥容器位置的信息。此外,您可以考虑在使用后直接删除私钥,只有在您确实需要使用它并且不能永久保留时才导入私钥。所有这一切对于提高安全性非常重要。

答案 1 :(得分:9)

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

X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet

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

并按如下方式安排私钥:

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

var rsaProvider = new RSACryptoServiceProvider(cspParams);

我希望这会有所帮助。

答案 2 :(得分:2)

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

此处提供更多信息:

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

丁科

答案 3 :(得分:0)

new X509Certificate2(localPFXPath,inputPass,X509KeyStorageFlags.MachineKeySet&amp; X509KeyStorageFlags.PersistKeySet);与&amp;而不是|为我工作。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

基本问题是.NET证书API只是C ++ advapi32证书管理器api的包装器,因此您无法指定传递给此api的所有选项,这些选项实际上是负责保留证书进入Windows证书库并持久保存密钥。底线是“ UseMachineStore ”选项需要传递给 CspProviderFlags ,然后传递给 CAPI.CRYPT_MACHINE_KEYSET 。这是确定密钥是否真实存在的小家伙。即使您设置 X509KeyStorageFlags.PersistKeySet MachineKeySet 以及可导出,似乎也有几个不同的原因导致此选项无法设置。只要愚蠢的密钥保留在C:\ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys \文件夹中,所有这些选项都有效。如果在导入时没有设置CRYPT_MACHINE_KEYSET,那么一旦GC处理了证书句柄,advapi32就会将密钥烧掉。

解决方案:在将证书导入个人计算机存储区之前,将证书添加到受信任的根目录。 In reading the logs from CAPI2,每次证书“导入”时,我实际上会看到两次调用“X509对象”。一个人总是有<Flags value="20" CRYPT_MACHINE_KEYSET="true"/>,(我们想要什么),但另一个不会有“验证链策略”没有返回任何错误。所以看起来 advapi32 正在检查证书的“有效性”,并且返回一个被X509Certificate2(I love how many empty catch blocks they have in that code)吞没的异常,或者advapi32只是单方面决定不保留不受信任的密钥证书。 (顺便说一下,我怀疑这是2008年和20012之间的行为变化,但我还没有证明这一点。)为了解决这个问题,我在我的代码中添加了一个If-check来添加证书,如果Issuer等于主题(它是一个自签名证书)然后将证书添加到Root,然后将其添加到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而不是将其移入时,它会触发条件将magic CRYPT_MACHINE_KEYSET标志传递给advapi32。