c# - "Invalid provider type specified"尝试加载证书私钥时出现CryptographicException

标签 c# windows certificate mmc certificate-store

我正在尝试读取第三方服务提供商与我共享的证书的私钥,因此我可以使用它来加密一些 XML,然后再通过网络将其发送给他们。我在 C# 中以编程方式这样做,但我认为这是权限或配置错误问题,因此我将重点关注似乎最相关的事实:

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

与我的同事不同,我之前曾多次尝试以各种方式卸载和重新安装证书,包括通过 IIS 管理器,还包括来自同一颁发者的旧证书。我在 MMC 中看不到任何旧证书或重复证书的痕迹。但是,我确实有许多相同大小的私钥文件,根据上次写入时间,这些文件在我进行了各种安装尝试后一定被遗留下来了。它们位于以下位置,分别用于本地计算机和当前用户存储:

c:\ProgramData\Microsoft\Crypto\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 所解释的那样 here .

Remy 使用 OpenSSL 和两个较旧的工具来完成私钥转换,我们希望将其自动化并开发了一个仅支持 OpenSSL 的解决方案。给定 MYCERT.pfx 和 CNG 格式的私钥密码 MYPWD,这些是在 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 命令行,"密码中的字符必须转义为 ""

关于c# - "Invalid provider type specified"尝试加载证书私钥时出现CryptographicException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22581811/

相关文章:

c# - 在 windows phone 7 模拟器上模拟连接不稳定进行测试

c# - ASP.NET Web API MVC 4 中带有特殊字符的自定义参数名称

c# - 将数据库上下文传递给静态方法

windows - 将硬盘盘符转换为相应的设备 ID(反之亦然)

Windows 中的 Python 主目录/多个版本的问题

azure - 如何验证 Azure 虚拟机 RDP 证书?

c# - 为什么 ModelState.IsValid 在 mvc 中总是返回 false

javascript - 如何替换当前打开的文件?

python - 在 Python ssl 客户端/服务器的情况下自动输入密码

amazon-web-services - 续订 IAM SSL 服务器证书