“尝试加载证书的私钥时,指定了”无效的提供程序types“CryptographicException

我试图读取由第三方服务提供商与我共享的证书的私钥,所以我可以使用它来encryption一些XML,然后再通过线路发送给他们。 我是用C#编程的,但我认为这是一个权限或configuration错误的问题,所以我将重点放在似乎最相关的事实上:

  • 我不认为这个问题是与代码有关的; 我的代码在其他计算机上工作,并且该问题影响来自Microsoft的示例代码。
  • 该证书是作为PFX文件提供的,仅用于testing目的,因此还包含虚拟证书颁发机构。
  • 使用MMC.exe,我可以将证书导入到本地计算机的个人存储中,然后授予所有相关帐户的私钥权限,并将证书颁发机构拖放到受信任的根证书颁发机构中。
  • 使用C#,我可以加载证书(通过其指纹识别),并使用X509Certificate2.HasPrivateKeyvalidation它是否具有私钥。 但是,尝试读取密钥会导致错误。 在.NET中,在尝试访问属性X509Certificate2.PrivateKey时,将引发CryptographicException消息“Invalid provider type specified”。 在Win32中,调用方法CryptAcquireCertificatePrivateKey返回等效的HRESULT, NTE_BAD_PROV_TYPE
  • 这是使用Microsoft自己的两个代码示例读取证书的私钥时也会发生的exception。
  • 在当前用户(而不是本地计算机)的等价存储中安装相同的证书,允许私钥成功加载。
  • 我在Windows 8.1本地pipe理员权限,我已经尝试在正常和高架模式下运行我的代码。 Windows 7和Windows 8上的同事已经能够从本地机器商店为同一证书加载密钥。
  • 我可以成功读取位于同一商店位置的自签名IIStesting证书的私钥。
  • 我已经瞄准了.NET 4.5(这个错误已经报告了一些旧版本的框架)。
  • 我不认为这是证书模板的问题,因为我希望同时影响本地计算机和当前用户商店?

与我的同事不同,我曾多次尝试以各种方式卸载和重新安装证书,包括通过IISpipe理器,还包括来自同一发行人的较旧证书。 在MMC中看不到任何旧的或重复的证书的痕迹。 但是,我有许多相同大小的私钥文件,基于最后写入时间,在我进行各种安装尝试之后,必须留下。 这些可在以下位置find,分别为本地机器和当前用户存储:

C:\ ProgramData \微软\encryption\ RSA \ MachineKeys的

c:\ Users \ \ AppData \ Roaming \ Microsoft \ Crypto \ RSA \ S-1-5-21- [其余用户ID]

所以,任何人都可以请告知是否:

  • 使用MMC卸载证书是一个好主意,删除所有那些看起来像孤立私钥的文件,然后重新安装证书并再试一次?
  • 还有其他的文件,我应该尝试手动删除?
  • 还有什么我应该尝试?

更新 – 添加了一个代码示例,显示尝试读取私钥:

 static void Main() { // Exception occurs when trying to read the private key after loading certificate from here: X509Store store = new X509Store("MY", StoreLocation.LocalMachine); // Exception does not occur if certificate was installed to, and loaded from, here: //X509Store store = new X509Store("MY", StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates; X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false); X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection); Console.WriteLine("Number of certificates: {0}{1}", scollection.Count, Environment.NewLine); foreach (X509Certificate2 x509 in scollection) { try { Console.WriteLine("Private Key: {0}", x509.HasPrivateKey ? x509.PrivateKey.ToXmlString(false) : "[N/A]"); x509.Reset(); } catch (CryptographicException ex) { Console.WriteLine(ex.Message); } } store.Close(); Console.ReadLine(); } 

我在Windows 8和server 2012/2012 R2上遇到了与最近收到的两个新证书相同的问题。 在Windows 10上,问题不再发生(但是这并不能帮助我,因为操作证书的代码在服务器上使用)。 虽然Joe Strommen的解决方案在原理上是可行的,但是不同的私钥模型需要使用证书对代码进行大规模的更改。 我发现一个更好的解决方案是将私钥从CNG转换为RSA,如Remy Blok 在这里所解释的。

Remy使用OpenSSL和两个较老的工具来完成私钥转换,我们希望自动化它,并开发了一个仅用于OpenSSL的解决方案。 给定CNG格式的私钥密码为MYPWD MYCERT.pfx ,这些步骤是以RSA格式和相同的密码获得具有私钥的新的CONVERTED.pfx

  1. 提取公钥,完整证书链:

OpenSSL pkcs12 -in "MYCERT.pfx" -nokeys -out "MYCERT.cer" -passin "pass:MYPWD"

  1. 提取私钥:

OpenSSL pkcs12 -in "MYCERT.pfx" -nocerts –out “MYCERT.pem" -passin "pass:MYPWD" -passout "pass:MYPWD"

  1. 将私钥转换为RSA格式:

OpenSSL rsa -inform PEM -in "MYCERT.pem" -out "MYCERT.rsa" -passin "pass:MYPWD" -passout "pass:MYPWD"

  1. 将公钥与RSA私钥合并为新的PFX:

OpenSSL pkcs12 -export -in "MYCERT.cer" -inkey "MYCERT.rsa" -out "CONVERTED.pfx" -passin "pass:MYPWD" -passout "pass:MYPWD"

如果您加载已转换的pfx或将其导入到Windows证书存储中,而不是CNG格式pfx,则问题消失,并且不需要更改C#代码。

在自动执行此操作时遇到了一个额外的问题:我们使用长私钥生成的密码,并且密码可能包含“。对于OpenSSL命令行”,密码内的字符必须以“”形式转义。

与亚历杭德罗博客的链接是关键。

我相信这是因为证书是使用CNG(“下一代加密”)API存储在您的机器上的。 旧的.NET API与它不兼容,所以不起作用。

您可以使用此API的Security.Cryptography包装( 在Codeplex上可用 )。 这将扩展方法添加到X509Certificate/X509Certificate2 ,所以你的代码看起来像这样:

 using Security.Cryptography.X509Certificates; // Get extension methods X509Certificate cert; // Populate from somewhere else... if (cert.HasCngKey()) { var privateKey = cert.GetCngPrivateKey(); } else { var privateKey = cert.PrivateKey; } 

不幸的是,CNG私钥的对象模型有很大的不同。 我不确定是否可以将它们导出为XML,就像在您的原始代码示例中一样…在我的情况下,我只需要使用私钥签名一些数据。

这是另一个可能发生的原因,这是一个奇怪的问题,经过一天的努力,我解决了这个问题。 作为实验,我更改了“C:\ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys”文件夹的权限,该文件夹使用机器密钥存储保存了证书的私钥数据。 当你改变这个文件夹的权限时,所有的私钥显示为不是提供者的“Microsoft Software KSP provider”(在我的情况下他们应该是“Microsoft RSA Schannel Cryptographic Provider”)。

解决方案:将权限重置为Machinekeys文件夹

这个文件夹的原始权限可以在这里找到。 在我的情况下,我已经改变了“所有人”的权限,给读取权限删除了“特殊权限”打勾。 所以我检查了我的一个团队成员(右键点击文件夹>属性>安全>高级>选择“所有人”>编辑>点击“高级设置”权限复选框列表

特殊权限

希望这会拯救别人的一天!

这里是我找到答案的来源 ,信用记录给他。

我也有这个问题,尝试了这个帖子中的建议,但没有成功。 通过使用Digicert证书实用程序https://www.digicert.com/util/重新加载证书,我能够解决我的问题。 这使得人们可以选择提供者来加载证书。 在我的情况下,将证书加载到Microsoft RSA Schannel密码提供程序提供程序中,我曾预料它将首先解决问题。