PrivateKey 引发了类型 System.Security.Cryptography.CryptographicException 的异常

PrivateKey threw an exception of type System.Security.Cryptography.CryptographicException

我正在尝试使用以下代码使用自签名证书:

X509Certificate2 cert = ToCertificate("CN=localhost");


public static X509Certificate2 ToCertificate(this string subjectName,
                                                StoreName name = StoreName.My,
                                                StoreLocation location = StoreLocation.LocalMachine
                                                )
    {
        X509Store store = new X509Store(name, location);

        store.Open(OpenFlags.ReadOnly);

        try
        {
            var cert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(c => c.Subject.Equals(subjectName, StringComparison.OrdinalIgnoreCase));

            return cert != null ? new X509Certificate2(cert) : null;
        }
        catch (Exception)
        {

            throw;
        }
        finally
        {
            store.Certificates.OfType<X509Certificate2>().ToList().ForEach(c => c.Reset());
            store.Close();
        }
    }

我收到以下异常:

PrivateKey = 'cert.PrivateKey' threw an exception of type 'System.Security.Cryptography.CryptographicException'

我试过了this fix, and this fix

但还是有问题!

听起来您的证书使用 CNG 密钥存储来存储私钥。在这种情况下,PrivateKey 属性 将在尝试访问 属性.

时抛出此异常

为了正确访问密钥,您必须使用扩展方法来访问密钥:https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2(v=vs.110).aspx#Extension方法

此外,在访问任何私钥存储类型(传统 (CSP) 或 CNG)时,这些扩展方法是首选。也就是说,不要直接访问 PrivateKeyPublicKey 属性,而是通过这些方法访问它们。

davidchristiansen 说:

What is a CNG Key? Certificates in Windows are stored using Storage Providers. Windows has two of these providers, that are not compatible. The old style “Cryptographic Service Providers” or CSP in short and the new style “Cryptography API: Next Generation” or CNG. The CNG providers have been around since Windows Vista, and although it is more secure and easier to use many facets of software are still not compatible with CNG providers. This appears to also include the .NET Framework.

A possible workaround to this may be to use CryptoAPI/CNG API directly to deal with CNG keys. But if we want an easier and pure .NET solution which understands CNG, we need to find another solution (details to follow!).

我按照以下 post 进行转换,将我的证书密钥从 CNG 转换为 RSA。有效!

http://blog.davidchristiansen.com/2016/05/521/

来自博客的步骤:

  1. Extract your public key and full certificate chain from your PFX file
  2. Extract the CNG private key
  3. Convert the private key to RSA format
  4. Merge public keys with RSA private key to a new PFX file

After changing your application to use the new PFX you just created, you should find that your issues have been resolved.

Now let’s see how to carry out these steps using OpenSSL (Get OpenSSL for Windows from here)

  1. Extract your public key and full certificate chain from your PFX file

OpenSSL pkcs12 -in "yourcertificate.pfx" -nokeys -out "yourcertificate.cer" -passin "pass:myreallystrongpassword"

  1. Extract the CNG private key

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

  1. Convert the private key to RSA format

OpenSSL rsa -inform PEM -in "yourcertificate.pem" -out "yourcertificate.rsa" -passin "pass:myreallystrongpassword" -passout "pass:myreallystrongpassword"

  1. Merge public keys with RSA private key to a new PFX file

OpenSSL pkcs12 -export -in "yourcertificate.cer" -inkey "yourcertificate.rsa" -out "yourcertificate-converted.pfx" -passin "pass:myreallystrongpassword" -passout "pass:myreallystrongpassword"

运行 在 IIS Express 上,程序使用您的凭据访问证书,而在 IIS 上,使用池标识的凭据。您可以轻松检查证书 ACL 以查看允许或不允许的人。

按照以下步骤操作:

  1. 检查您的网站使用的应用程序池

打开 Internet 信息服务管理器,select 左侧连接树中的站点。 Select 在中间面板中您的站点,然后单击右侧面板上操作下的基本设置。

  1. 检查应用程序池使用的身份

Select Application Pools 在左侧的Connections 树中,并在中间的面板中找到identity。可能是 "NETWORK SERVICE".

  1. 将应用程序池使用的身份的读取权限添加到您的证书

打开 Microsoft 管理控制台 (mmc),为本地计算机帐户添加证书管理单元,然后在个人证书下找到您的证书。打开其上下文菜单,所有任务和管理私钥...。单击添加...,输入身份 ("NETWORK SERVICE"),然后单击检查名称和确定。在权限下仅允许读取权限。

您可以在这个问题中阅读详细信息:How to give ASP.NET access to a private key in a certificate in the certificate store?

参考:

如果您正在调试应用程序,请尝试以管理员身份打开 Visual Studio。它解决了我的问题。