【问题标题】:Getting Exception "Invalid Provider type specified" or "Key does not exist" while getting Private key from X509Certificate2 Occasionally偶尔从 X509Certificate2 获取私钥时出现异常“指定的提供程序类型无效”或“密钥不存在”
【发布时间】:2018-01-08 14:31:42
【问题描述】:

我在尝试从 X509Certificate2 证书获取私钥时遇到以下异常之一:

System.Security.Cryptography.CryptographicException:指定的提供程序类型无效。

System.Security.Cryptography.CryptographicException:以下代码行中不存在密钥:RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)digiSignCert.PrivateKey;

堆栈跟踪:

System.Security.Cryptography.CryptographicException:密钥不存在。在 System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters 参数, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle) 在 System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair() 在 System.Security.Cryptography.RSACryptoServiceProvider。 .ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize) at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey() at Api.CertificateUtil.GetSignedXml(String xml, X509Certificate2 privateCert)

代码:

public static RSACryptoServiceProvider rsaKey = null;
public X509Certificate2 _PrivateCert;

public APISearch()
{
    byte[] privateCert = null;//We get the actual certificate file data here
    GetPrivateCerificate(privateCert, "abc@123");
    GetSignedXml(_PrivateCert);
}

public void GetPrivateCerificate(byte[] privateCert, string pwd)
{
    _PrivateCert = new X509Certificate2(privateCert, pwd, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
}

public void GetSignedXml(X509Certificate2 privateCert)
{
    rsaKey = (RSACryptoServiceProvider)privateCert.PrivateKey; //Occassional Exception
}

预期结果:(RSACryptoServiceProvider)privateCert.PrivateKey 应始终生成私钥。

实际结果:有时会在这一行抛出上述异常:

rsaKey = (RSACryptoServiceProvider)privateCert.PrivateKey;

有时会从证书文件中成功获取私钥。截至目前,我们无法追踪此问题的模式。

【问题讨论】:

    标签: c# cryptography x509certificate x509certificate2 rsacryptoserviceprovider


    【解决方案1】:

    RSACryptoServiceProvider 是一种通过 Window Cryptographic API (CAPI) 库执行 RSA 的类型。当 .NET 首次创建时,CAPI 是新的并且始终是正确的答案(在 Windows 上)。从 Windows Vista 开始,有一个新库:密码学:下一代 (CNG)。为了兼容性,CNG 了解如何使用 CAPI。但CAPI不能“成为CAPI”和“懂CNG”。您看到的例外情况是 PFX 指示私钥应通过 CNG 存储(或店内证书指示其私钥通过 CNG 存储)。

    当 .NET Framework 添加RSACng 时,它决定已经有太多人编写了(RSACryptoServiceProvider)cert.PrivateKey 行,因此该属性永远无法返回RSACng 实例。相反,在 .NET 4.6 中创建了新的(扩展)方法:cert.GetRSAPublicKey()cert.GetRSAPrivateKey(),它们返回 RSA 而不是 AsymmetricAlgorithm。同样在 .NET 4.6 中,RSA 基类得到了增强,将签名/验证和加密/解密操作向下移动(尽管签名不同,因为自 CAPI 编写以来 RSA 获得了新的选项)。

    预期结果:(RSACryptoServiceProvider)privateCert.PrivateKey 应始终生成私钥。

    实际情况是cert.PrivateKey(和cert.PublicKey.Key)已被软弃用。你不应该再叫它/他们了。 RSA (4.6)、ECDSA (4.6.1) 和 DSA (4.6.2) 都有 Get[Algorithm]{Public|Private}Key 方法。

    • (RSACryptoServiceProvider)cert.PrivateKey => cert.GetRSAPrivateKey()
    • rsaCSP.Encrypt(data, false) => rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1)
    • rsaCSP.Encrypt(data, true) => rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA1)
    • rsaCSP.SignData(data, "SHA256") => rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)

    DecryptSignHashVerifyDataVerifyHash 类似; ECDsaDSA 也类似。

    最后,请不要硬转换这些方法的返回值,它会根据需要更改...在 Windows 上它可以返回 RSACng 或 RSACryptoServiceProvider,在 Linux(.NET Core)上它当前返回 RSAOpenSsl,在 macOS (.NET Core) 上,它返回一个不可转换的对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-23
      • 2017-09-20
      • 1970-01-01
      相关资源
      最近更新 更多