InvalidCastException: Unable to cast object of type 'System.Security.Cryptography.RSACng'

3

I'm trying to pass the PIN through the code, so the user does not have to type it all the time, he's returning this error:

  InvalidCastException: Unable to cast object of type 'System.Security.Cryptography.RSACng'

This is the function I'm using:

public static RSACryptoServiceProvider LerDispositivo(RSACryptoServiceProvider key, string PIN)
{
    CspParameters csp = new CspParameters(key.CspKeyContainerInfo.ProviderType, key.CspKeyContainerInfo.ProviderName);
    SecureString ss = new SecureString();
    foreach (char a in PIN)
    {
        ss.AppendChar(a);
    }
    csp.ProviderName = key.CspKeyContainerInfo.ProviderName;
    csp.ProviderType = key.CspKeyContainerInfo.ProviderType;
    csp.KeyNumber = key.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2;
    csp.KeyContainerName = key.CspKeyContainerInfo.KeyContainerName;
    csp.KeyPassword = ss;
    csp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseDefaultKeyContainer;

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
    return rsa;
}

And I've added these 3 lines in my sign-in function:

RSACryptoServiceProvider Key = new RSACryptoServiceProvider();
                    Key = (System.Security.Cryptography.RSACryptoServiceProvider)x509Cert.PrivateKey;
                    signedXml.SigningKey = x509Cert.PrivateKey;
                    signedXml.SigningKey = LerDispositivo(Key, "senhaaqui");

The error occurs on this line:

 Key = (System.Security.Cryptography.RSACryptoServiceProvider)x509Cert.PrivateKey;

EDIT I've checked several examples on the internet, but none solved my problem. I was able to find this #

Following the LINK above, I tried to do this:

RSACryptoServiceProvider publicKeyProvider = (System.Security.Cryptography.RSACryptoServiceProvider)x509Cert.GetRSAPrivateKey();
                        signedXml.KeyInfo = keyInfo;
                        signedXml.SigningKey = LerDispositivo(publicKeyProvider, "senhaaqui");

and it returns the same error:

  InvalidCastException: Unable to cast object of type 'System.Security.Cryptography.RSACng' to type 'System.Security.Cryptography.RSACryptoServiceProvider'.

Is there any way to convert RSACryptoServiceProvider to RSACng? Every way I try returns the same error.

EDIT

According to Pedro's response, I made the changes, but still without success, it returns the following error:

  

error CS0433: The type "CngPropertyOptions" exists in "System.Security.Cryptography.Algorithms, Version = 4.3.1.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a" and "System.Security.Cryptography.Cng, Version = 4.3. 1.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a "

I have tried several "solutions" that could solve the problem, but none have solved the problem yet.

    
asked by anonymous 30.11.2018 / 18:28

1 answer

4

In Windows there are two generations of encryption APIs:

  • The old generation ( link ), which includes Cryptographic Service Providers (CSP), which is the technology used by the class RSACryptoServiceProvider ;
  • And the new generation ( link ), Cryptography API : Next Generation (CNG), which is the technology used by the class RSACng .

The X509Certificate2.PrivateKey property returns a object of type AsymmetricAlgorithm , which can be of type RSA or DSA . From the .NET Framework 4.6 you can use the extension method X509Certificate2.GetRSAPrivateKey , which returns an object directly RSA .

A certificate key can implement the two cryptographic generations, or just one of them. In the case of your certificate, it seems that only the new cryptographic generation is implemented, so the X509Certificate2.PrivateKey property is returning an object of type RSACng , which can not be directly converted to type RSACryptoServiceProvider , since they are totally different.

Only the routine you have to enter the PIN of your hardware certificate only works with type RSACryptoServiceProvider , so you will have to adapt it to RSACng type. I do not have a hardware certificate here to test, but I wrote a routine for you to test.

First the LerDispositivo() function (keep the name you used, but now you pass the X509Certificate2 certificate directly to it) to see what type of RSA is received from the certificate, if it is CNG ( Cryptography API: Next Generation ) or CSP Cryptographic Service Provider ), and then redirects to the function that knows how deal with each case:

using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

public static RSA LerDispositivo(X509Certificate2 cert, string pin)
{
   // Obtém a chave privada RSA do certificado.
   RSA rsa = cert.GetRSAPrivateKey();

   // Se a chave for do tipo CNG chama a função que trata desse tipo.
   RSACng rsaCng = rsa as RSACng;
   if (rsaCng != null)
   {
      return LerDispositivoCng(rsaCng, pin);
   }

   // Se a chave for do tipo CSP chama a função que trata desse tipo.
   RSACryptoServiceProvider rsaCsp = rsa as RSACryptoServiceProvider;
   if (rsaCsp != null)
   {
      return LerDispositivoCsp(rsaCsp, pin);
   }

   // A chave não era nem CNG nem CSP.
   return null;
}

private static RSA LerDispositivoCng(RSACng rsaCng, string pin)
{
   byte[] propValue;

   // O valor da propriedade deve ser informado como um array de bytes em
   // formato Unicode, e deve contar um caractere nulo como terminador.
   if (pin[pin.Length - 1] == '
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

public static RSA LerDispositivo(X509Certificate2 cert, string pin)
{
   // Obtém a chave privada RSA do certificado.
   RSA rsa = cert.GetRSAPrivateKey();

   // Se a chave for do tipo CNG chama a função que trata desse tipo.
   RSACng rsaCng = rsa as RSACng;
   if (rsaCng != null)
   {
      return LerDispositivoCng(rsaCng, pin);
   }

   // Se a chave for do tipo CSP chama a função que trata desse tipo.
   RSACryptoServiceProvider rsaCsp = rsa as RSACryptoServiceProvider;
   if (rsaCsp != null)
   {
      return LerDispositivoCsp(rsaCsp, pin);
   }

   // A chave não era nem CNG nem CSP.
   return null;
}

private static RSA LerDispositivoCng(RSACng rsaCng, string pin)
{
   byte[] propValue;

   // O valor da propriedade deve ser informado como um array de bytes em
   // formato Unicode, e deve contar um caractere nulo como terminador.
   if (pin[pin.Length - 1] == '%pre%')
   {
      propValue = Encoding.Unicode.GetBytes(pin);
   }
   else
   {
      propValue = new byte[Encoding.Unicode.GetByteCount(pin) + 2];
      Encoding.Unicode.GetBytes(pin, 0, pin.Length, propValue, 0);
   }

   const string NCRYPT_PIN_PROPERTY = "SmartCardPin";

   CngProperty prop = new CngProperty(NCRYPT_PIN_PROPERTY,
                                      propValue,
                                      CngPropertyOptions.None);
   rsaCng.Key.SetProperty(prop);

   return rsaCng;
}

private static RSA LerDispositivoCsp(RSACryptoServiceProvider rsaCsp, string pin)
{
   var securePin = new SecureString();
   foreach (char c in pin)
   {
      securePin.AppendChar(c);
   }

   var cspParams = new CspParameters(
                           rsaCsp.CspKeyContainerInfo.ProviderType,
                           rsaCsp.CspKeyContainerInfo.ProviderName,
                           rsaCsp.CspKeyContainerInfo.KeyContainerName,
                           null,
                           securePin);
   cspParams.KeyNumber = (int) KeyNumber.Signature;
   cspParams.Flags = CspProviderFlags.NoPrompt |
                     CspProviderFlags.UseDefaultKeyContainer;

   return new RSACryptoServiceProvider(cspParams);
}
') { propValue = Encoding.Unicode.GetBytes(pin); } else { propValue = new byte[Encoding.Unicode.GetByteCount(pin) + 2]; Encoding.Unicode.GetBytes(pin, 0, pin.Length, propValue, 0); } const string NCRYPT_PIN_PROPERTY = "SmartCardPin"; CngProperty prop = new CngProperty(NCRYPT_PIN_PROPERTY, propValue, CngPropertyOptions.None); rsaCng.Key.SetProperty(prop); return rsaCng; } private static RSA LerDispositivoCsp(RSACryptoServiceProvider rsaCsp, string pin) { var securePin = new SecureString(); foreach (char c in pin) { securePin.AppendChar(c); } var cspParams = new CspParameters( rsaCsp.CspKeyContainerInfo.ProviderType, rsaCsp.CspKeyContainerInfo.ProviderName, rsaCsp.CspKeyContainerInfo.KeyContainerName, null, securePin); cspParams.KeyNumber = (int) KeyNumber.Signature; cspParams.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseDefaultKeyContainer; return new RSACryptoServiceProvider(cspParams); }

References:

  
    
04.12.2018 / 17:13