2010-06-30 13 views
6

Próbuję szyfrować i odszyfrowywać dane przy użyciu RSA w języku C#. Mam następujący test jednostki MSTest:Kryptograficzny wyjątek pojawia się sporadycznie podczas szyfrowania/odszyfrowywania za pomocą 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"); 

Błąd podczas odszyfrowywania, ale tylko czasami. Wygląda na to, że za każdym razem, gdy ustawiam punkty przerwania i przechodzę test, przechodzi. To sprawiło, że pomyślałem, że być może coś nie zakończyło się na czas, aby odszyfrować pomyślnie, a ja spowalniając przechodzenie przez niego podczas debugowania dało mu wystarczająco dużo czasu na dokończenie. Gdy to się nie powiedzie, linia wydaje się nie na to decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); w następujący sposób:

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); 
} 

To nie z tego wyjątku:

System.Security.Cryptography.CryptographicException: Bad danych

Moja Encrypt sposób jest następujący:

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); 
} 

ByteConverter używane w całym tylko następujące:

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(); 

Widziałem kilka pytań na StackOverflow dotyczące szyfrowania i deszyfrowania RSA z .NET. This one było spowodowane zaszyfrowaniem kluczem prywatnym i próbą odszyfrowania za pomocą klucza publicznego, ale nie sądzę, żebym to zrobił. This question ma taki sam wyjątek jak ja, ale wybraną odpowiedzią było użycie OpenSSL.NET, którego wolałbym nie robić.

Co robię źle?

Odpowiedz

8

Czy można zastąpić ByteConverter.GetBytes przez Convert.FromBase64String i wymienić ByteConverter.GetString na Convert.ToBase64String i sprawdzić, czy to pomaga. Bad Data wyjątek oznacza zwykle, że w danych jest niepoprawny znak lub że długość nie jest poprawną długością do odszyfrowania. Myślę, że używanie funkcji Convert może rozwiązać twoje problemy.

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, próba, która daje mi inny wyjątek: "System.FormatException: Niepoprawna długość dla tablicy znaków Base-64". To miało miejsce w pierwszym wierszu 'Encrypt':' byte [] bytesToEncrypt = Convert.FromBase64String (textToEncrypt); '. –

+1

@Sarah - Ok, zaktualizowałem twój przykład. Testowałem to i wygląda na to, że działa. – SwDevMan81

+0

To działa! Dzięki. Nigdy bym nie pomyślał o użyciu tej kombinacji 'Convert' /' UnicodeEncoding'. –

1

polecam korzystania z tej klasy, niestety nie pamiętam autora oryginalnego choć ..

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; 
     } 

    } 
} 
} 

Użyj jako:

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

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

EDIT: Polecam również, że nie używa się tego do szyfrowania dużych danych. Zwykle zaszyfrowalibyśmy rzeczywiste dane za pomocą algorytmu symetrycznego (AES, itd.), A następnie szyfrowali klucz symetryczny (generowany losowo) za pomocą algorytmu RSA, a następnie wysyłali zaszyfrowany klucz symetryczny RSA oraz dane klucza symetrycznego .. Powinieneś również spójrz na podpisywanie RSA, aby upewnić się, że dane pochodzą z miejsca, w którym jest napisane ..

3

Twój problem dotyczy konwersji z bajtów na łańcuch. Nie wszystkie sekwencje bajtów są poprawnym kodowaniem UTF-16 i używasz UnicodeEncoding, który po cichu ignoruje nieprawidłowe bajty.Jeśli użyto

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

zamiast tego kodu nie doszłoby do skutku, gdy próbuje przekonwertować bajty zamiast cicho zastąpienie nieprawidłowych bajt pary z 0xFFFD.

Fakt, że test zadziałał podczas debugowania, był zbiegiem okoliczności. Używasz losowej pary kluczy RSA, więc czasami otrzymasz szyfrowanie, które jest poprawnym kodowaniem UTF-16.

Poprawka jest taka, jak sugeruje SwDevMan81, do użycia kodowania, które może konwertować wszystkie możliwe tablice bajtowe. F.x. Kodowanie Base64.

Powiązane problemy