2010-06-30 14 views
6

Sto tentando di crittografare e decrittografare i dati utilizzando RSA in C#. Ho il seguente test dell'unità MSTest:CryptographicException si verifica in modo intermittente durante la crittografia/decrittografia con RSA

const string rawPassword = "mypass"; 

// Encrypt 
string publicKey, privateKey; 
string encryptedPassword = RSAUtils.Encrypt(rawPassword, out publicKey, out privateKey); 
Assert.AreNotEqual(rawPassword, encryptedPassword, 
    "Raw password and encrypted password should not be equal"); 

// Decrypt 
string decryptedPassword = RSAUtils.Decrypt(encryptedPassword, privateKey); 
Assert.AreEqual(rawPassword, decryptedPassword, 
    "Did not get expected decrypted password"); 

Non riesce durante la decodifica, ma solo qualche volta. Sembra che ogni volta che imposto i breakpoint e passo attraverso il test, passi. Questo mi ha fatto pensare che forse qualcosa non si stava concludendo in tempo per la decifrazione e che ho rallentato l'esecuzione mentre il debug ha dato il tempo necessario per completarlo. Quando non riesce, la linea sembra di non riuscire a è decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); nel seguente metodo:

public static string Decrypt(string textToDecrypt, string privateKeyXml) 
{ 
    if (string.IsNullOrEmpty(textToDecrypt)) 
    { 
     throw new ArgumentException(
      "Cannot decrypt null or blank string" 
     ); 
    } 
    if (string.IsNullOrEmpty(privateKeyXml)) 
    { 
     throw new ArgumentException("Invalid private key XML given"); 
    } 
    byte[] bytesToDecrypt = ByteConverter.GetBytes(textToDecrypt); 
    byte[] decryptedBytes; 
    using (var rsa = new RSACryptoServiceProvider()) 
    { 
     rsa.FromXmlString(privateKeyXml); 
     decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here 
    } 
    return ByteConverter.GetString(decryptedBytes); 
} 

non riesce con questa eccezione:

System.Security.Cryptography.CryptographicException: Bad dati

Il mio metodo Encrypt è il seguente:

public static string Encrypt(string textToEncrypt, out string publicKey, 
    out string privateKey) 
{ 
    byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt); 
    byte[] encryptedBytes; 
    using (var rsa = new RSACryptoServiceProvider()) 
    { 
     encryptedBytes = rsa.Encrypt(bytesToEncrypt, false); 
     publicKey = rsa.ToXmlString(false); 
     privateKey = rsa.ToXmlString(true); 
    } 
    return ByteConverter.GetString(encryptedBytes); 
} 

Il ByteConverter utilizzato in tutto è proprio la seguente:

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(); 

Ho visto alcune domande su StackOverflow su crittografia RSA e la decrittografia con .NET. This one era dovuto alla crittografia con la chiave privata e al tentativo di decifrare con la chiave pubblica, ma non credo che lo stia facendo. This question ha la stessa eccezione di me, ma la risposta selezionata era usare OpenSSL.NET, che preferirei non fare.

Cosa sto sbagliando?

risposta

8

Potrebbe sostituire ByteConverter.GetBytes con Convert.FromBase64String e sostituire ByteConverter.GetString con Convert.ToBase64String e vedere se questo aiuta. L'eccezione Bad Data di solito significa che si dispone di un carattere non valido nei dati o che la lunghezza non è la lunghezza corretta per la decrittografia. Penso che l'uso delle funzioni Converti potrebbe risolvere i tuoi problemi.

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(); 

    public static string Encrypt(string textToEncrypt, out string publicKey, 
    out string privateKey) 
    { 
    byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt); 
    byte[] encryptedBytes; 
    using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) 
    { 
     encryptedBytes = rsa.Encrypt(bytesToEncrypt, false); 
     publicKey = rsa.ToXmlString(false); 
     privateKey = rsa.ToXmlString(true); 
    } 
    return Convert.ToBase64String(encryptedBytes); 
    } 

    public static string Decrypt(string textToDecrypt, string privateKeyXml) 
    { 
    if (string.IsNullOrEmpty(textToDecrypt)) 
    { 
     throw new ArgumentException(
      "Cannot decrypt null or blank string" 
     ); 
    } 
    if (string.IsNullOrEmpty(privateKeyXml)) 
    { 
     throw new ArgumentException("Invalid private key XML given"); 
    } 
    byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt); 
    byte[] decryptedBytes; 
    using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) 
    { 
     rsa.FromXmlString(privateKeyXml); 
     decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here 
    } 
    return ByteConverter.GetString(decryptedBytes); 
    } 
+1

Hm, provando questo mi dà un'eccezione diversa: "System.FormatException: lunghezza non valida per un array di char Base-64". Questo si è verificato sulla prima riga di 'Encrypt':' byte [] bytesToEncrypt = Convert.FromBase64String (textToEncrypt); '. –

+1

@Sarah - Ok, ho aggiornato il tuo esempio. L'ho provato e sembra liek funziona. – SwDevMan81

+0

Funziona! Grazie. Non avrei mai pensato di usare quel mix di 'Convert' /' UnicodeEncoding'. –

1

mi consiglia di utilizzare questa classe, purtroppo non ricordo l'autore originale però ..

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 

namespace Encryption 
{ 

class AsymmetricED 
{ 
    private static RSAParameters param = new RSAParameters(); 
    /// <summary> 
    /// Get Parameters 
    /// </summary> 
    /// <param name="pp">Export private parameters?</param> 
    /// <returns></returns> 
    public static RSAParameters GenerateKeys(bool pp) 
    { 
     RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
     if (param.Equals(new RSAParameters())) 
     { 
      param = RSA.ExportParameters(true); 
     } 
     RSA.ImportParameters(param); 
     return RSA.ExportParameters(pp); 
    } 
    static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding) 
    { 
     try 
     { 
      //Create a new instance of RSACryptoServiceProvider. 
      RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 

      //Import the RSA Key information. This only needs 
      //toinclude the public key information. 
      RSA.ImportParameters(RSAKeyInfo); 

      //Encrypt the passed byte array and specify OAEP padding. 
      //OAEP padding is only available on Microsoft Windows XP or 
      //later. 
      return RSA.Encrypt(DataToEncrypt, DoOAEPPadding); 
     } 
     //Catch and display a CryptographicException 
     //to the console. 
     catch (CryptographicException e) 
     { 
      Console.WriteLine(e.Message); 

      return null; 
     } 

    } 

    static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding) 
    { 
     try 
     { 
      //Create a new instance of RSACryptoServiceProvider. 
      RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 

      //Import the RSA Key information. This needs 
      //to include the private key information. 
      RSA.ImportParameters(RSAKeyInfo); 

      //Decrypt the passed byte array and specify OAEP padding. 
      //OAEP padding is only available on Microsoft Windows XP or 
      //later. 
      return RSA.Decrypt(DataToDecrypt, DoOAEPPadding); 
     } 
     //Catch and display a CryptographicException 
     //to the console. 
     catch (CryptographicException e) 
     { 
      ConsoleColor col = Console.BackgroundColor; 
      Console.BackgroundColor = ConsoleColor.Red; 
      Console.WriteLine(e.ToString()); 
      Console.BackgroundColor = col; 
      return null; 
     } 

    } 
} 
} 

Usa come:

Encryption.AsymmetricED.RSAEncrypt(Data, GenerateKeys(false), false); 

Encryption.AsymmetricED.RSADecrypt(Data, GenerateKeys(true), false); 

EDIT: Mi consiglia inoltre di non lo usi per la crittografia dei dati di grandi dimensioni. Normalmente si crittografa i dati effettivi con un algoritmo simmetrico (AES, ecc.), Quindi si cripta la chiave simmetrica (generata casualmente) con l'algoritmo RSA, quindi si invia la chiave simmetrica crittografata rsa ei dati della chiave simmetrica. Si dovrebbe anche guarda la firma RSA, per assicurarti che i dati provengano da dove dice che è ..

3

Il tuo problema è con la conversione da byte a stringa. Non tutte le sequenze di byte sono una codifica UTF-16 valida e si sta utilizzando un UnicodeEncoding che ignora silenziosamente i byte non validi.Se è stato utilizzato

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(false, false, true); 

invece, il codice avrebbe mancato quando si cerca di convertire i byte invece di sostituire in silenzio i byte coppie non validi con 0xFFFD.

Il fatto che il test abbia funzionato durante il debug è stata una coincidenza. Stai utilizzando una coppia di chiavi RSA casuale, quindi a volte otterrai una codifica che è una codifica UTF-16 valida.

La correzione è, come suggerisce SwDevMan81, per utilizzare una codifica in grado di convertire tutti i possibili array di byte. F.x. Base64 codifica.

Problemi correlati