从X509Certificate2获取私钥时获取异常"指定的提供程序类型无效"或"密钥不存在"

Nar*_*shi 2 c# cryptography rsacryptoserviceprovider x509certificate2 x509certificate

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

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

要么

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

堆栈跟踪:

System.Security.Cryptography.CryptographicException:键不存在.在System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType关键字类型,CspParameters参数,布尔randomKeyContainer,的Int32 dwKeySize,SafeProvHandle&safeProvHandle,SafeKeyHandle&safeKeyHandle)在System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()在System.Security.Cryptography.RSACryptoServiceProvider.位于Api.CertificateUtil.GetSignedXml(String xml,X509Certificate2 privateCert)的System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()中的.ctor(Int32 dwKeySize,CspParameters参数,布尔值useDefaultKeySize)

码:

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
}
Run Code Online (Sandbox Code Playgroud)

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

实际结果:有时上述异常会抛出此行:

rsaKey = (RSACryptoServiceProvider)privateCert.PrivateKey;

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

bar*_*njs 7

RSACryptoServiceProvider是一种通过Window Cryptographic API(CAPI)库执行RSA的类型.首次创建.NET时,CAPI是新的,总是正确答案(在Windows上).从Windows Vista开始,有一个新的库:​​Cryptography:Next Generation(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基类得到了增强,可以将Sign/Verify和Encrypt/Decrypt操作向下移动(虽然具有不同的签名,因为自从编写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)

类似的Decrypt,SignHash,VerifyData,VerifyHash,和类似的ECDsaDSA.

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