C# “获取异常”;指定的提供程序类型无效";或;“密钥不存在”;偶尔从X509Certificate2获取私钥时

C# “获取异常”;指定的提供程序类型无效";或;“密钥不存在”;偶尔从X509Certificate2获取私钥时,c#,cryptography,x509certificate,x509certificate2,rsacryptoserviceprovider,C#,Cryptography,X509certificate,X509certificate2,Rsacryptoserviceprovider,尝试从X509Certificate2证书获取私钥时,我遇到以下异常之一: System.Security.Cryptography.CryptographyException:指定的提供程序类型无效 或 System.Security.Cryptography.CryptographyException:以下代码行中不存在密钥:rsacyptoServiceProvider rsaKey=(rsacyptoServiceProvider)digiSignCert.PrivateKey 堆栈跟踪

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

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

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

堆栈跟踪:

System.Security.Cryptography.CryptographyException:密钥不存在。在System.Security.Cryptography.Utils.GetKeyPairHelper(CsAlgorithmType keyType、CsParameters parameters、Boolean randomKeyContainer、Int32 dwKeySize、SafeProvHandle和SafeProvHandle、SafeKeyHandle和SafeKeyHandle)在System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()处在System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()的Api.CertificateUtil.GetSignedXml(字符串xml,X509Certificate2 privateCert)

代码:

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

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

rsaKey=(rsacyptoserviceprovider)privateCert.PrivateKey;


有时会从证书文件成功获取私钥。到目前为止,我们无法跟踪此问题的模式。

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

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

预期结果:
(RSACryptServiceProvider)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,rsacencryptionpadding.Pkcs1)
  • rsaCSP.Encrypt(data,true)
    =>
    rsa.Encrypt(data,rsacencryptionpadding.OaepSHA1)
  • rsaCSP.SignData(data,“SHA256”)
    =>
    rsa.SignData(data,HashAlgorithmName.SHA256,rsasignatureadding.Pkcs1)
类似于
Decrypt
SignHash
VerifyData
VerifyHash
;类似于
ECDsa
DSA

最后,请不要强制转换这些方法的返回值,它会根据需要进行更改…在Windows上,它可以返回RSACng或RSACryptServiceProvider,在Linux(.NET Core)上,它当前返回RSAOpenSsl,在macOS(.NET Core)上,它返回不可压缩的对象

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
}