2010-06-30 48 views
6

Tôi đang cố mã hóa và giải mã dữ liệu bằng RSA trong C#. Tôi có bài kiểm tra đơn vị MSTest sau:CryptographicException không liên tục xảy ra khi mã hóa/giải mã bằng 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"); 

Không thành công trong quá trình giải mã, nhưng đôi khi. Nó có vẻ như bất cứ khi nào tôi đặt breakpoint và bước qua các thử nghiệm, nó đi qua. Điều này làm cho tôi nghĩ có lẽ một cái gì đó đã không được hoàn thành trong thời gian để giải mã xảy ra thành công, và tôi làm chậm bước qua nó trong khi gỡ lỗi đã cho nó đủ thời gian để hoàn thành. Khi nó không thành công, dòng có vẻ như thất bại tại là decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); bằng phương pháp sau:

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

Nó không thành công với ngoại lệ này:

System.Security.Cryptography.CryptographicException: Xấu dữ liệu

phương pháp Encrypt của tôi là như sau:

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

Các ByteConverter sử dụng trong suốt chỉ như sau:

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(); 

Tôi đã nhìn thấy một vài câu hỏi về StackOverflow về mã hóa RSA và giải mã với .NET. This one là do mã hóa bằng khóa riêng và cố gắng giải mã bằng khóa công khai, nhưng tôi không nghĩ mình đang làm điều đó. This question có cùng ngoại lệ với tôi, nhưng câu trả lời được chọn là sử dụng OpenSSL.NET, mà tôi không muốn làm.

Tôi đang làm gì sai?

Trả lời

8

Bạn có thể thay thế ByteConverter.GetBytes bằng Convert.FromBase64String và thay thế ByteConverter.GetString bằng Convert.ToBase64String và xem điều đó có giúp ích hay không. Bad Data ngoại lệ thường có nghĩa là bạn có ký tự không hợp lệ trong dữ liệu hoặc độ dài không phải là độ dài chính xác để giải mã. Tôi nghĩ rằng việc sử dụng chức năng Chuyển đổi có thể khắc phục được sự cố của bạn.

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, việc thử cung cấp cho tôi một ngoại lệ khác: "System.FormatException: Độ dài không hợp lệ đối với mảng char Base-64". Điều này xảy ra trên dòng đầu tiên của 'Encrypt':' byte [] bytesToEncrypt = Convert.FromBase64String (textToEncrypt); '. –

+1

@Sarah - Ok, tôi đã cập nhật ví dụ của bạn. Tôi đã thử nghiệm nó ra và nó trông liek nó hoạt động. – SwDevMan81

+0

Điều đó hoạt động! Cảm ơn. Tôi sẽ không bao giờ nghĩ đến việc sử dụng kết hợp của 'Convert' /' UnicodeEncoding'. –

1

tôi sẽ khuyên bạn sử dụng lớp này, thật đáng buồn tôi không nhớ tác giả ban đầu mặc dù ..

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

    } 
} 
} 

Use as:

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

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

EDIT: Tôi cũng khuyên bạn không sử dụng điều này để mã hóa dữ liệu lớn. Thông thường, bạn sẽ mã hóa dữ liệu thực tế bằng thuật toán đối xứng (AES, vv), sau đó mã hóa khóa đối xứng (được tạo ngẫu nhiên) bằng thuật toán RSA, sau đó gửi khóa đối xứng được mã hóa rsa và dữ liệu khóa đối xứng .. Bạn cũng nên nhìn vào ký kết RSA, để đảm bảo dữ liệu đến từ nơi nó nói là ..

3

Vấn đề của bạn là chuyển đổi từ byte thành chuỗi. Không phải tất cả các chuỗi byte đều là mã hóa UTF-16 hợp lệ và bạn đang sử dụng UnicodeEncoding để bỏ qua các byte không hợp lệ.Nếu bạn đã sử dụng

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

thay vào đó, mã của bạn sẽ thất bại khi cố chuyển đổi byte thay vì âm thầm thay thế các cặp byte không hợp lệ bằng 0xFFFD.

Thực tế là thử nghiệm đã hoạt động trong khi gỡ lỗi là trùng hợp ngẫu nhiên. Bạn đang sử dụng cặp khóa RSA ngẫu nhiên, vì vậy đôi khi bạn sẽ nhận được mã hóa là mã hóa UTF-16 hợp lệ.

Khắc phục, như SwDevMan81 đề xuất, sử dụng mã hóa có thể chuyển đổi tất cả các mảng byte có thể. F.x. Mã hóa Base64.

Các vấn đề liên quan