入门异常“无效的提供程序类型指定”或“项不存在”,而从X509Certificate2获得私钥偶尔

问题描述:

我得到以下例外之一,而试图从X509Certificate2证书私钥:入门异常“无效的提供程序类型指定”或“项不存在”,而从X509Certificate2获得私钥偶尔

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

OR

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..ctor(的Int32 dwKeySize,CspParameters参数,布尔useDefaultKeySize)在System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()在Api.CertificateUtil.GetSignedXml(字符串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, "[email protected]"); 
    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;

,有时私钥被成功从证书文件中取得。截至目前,我们一直无法跟踪这个问题的模式。

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基类已得到增强,使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}键方法。

  • (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)上返回一个不可播放的对象。