2010-06-30 20 views
6

我試圖使用C#中的RSA加密和解密數據。我有以下MSTest單元測試:使用RSA加密/解密時間斷髮生CryptographicException

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

它在解密過程中失敗,但僅在有時。似乎每當我設置斷點並逐步通過測試時,它就會通過。這讓我想,或許有些事情沒有及時完成解密,而且我在調試的時候放緩了腳步,足以讓它有足夠的時間完成。當它失敗時,它似乎在失敗行是下面的方法decryptedBytes = rsa.Decrypt(bytesToDecrypt, false);

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

它失敗與此異常:

System.Security.Cryptography.CryptographicException:壞數據

Encrypt方法如下:

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就是以下幾點:

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(); 

我已經看到了有關RSA加密和解密使用.NET在計算器上的一些問題。 This one是由於使用私鑰進行加密並試圖用公鑰解密,但我認爲我沒有這樣做。 This question與我有同樣的例外,但選擇的答案是使用OpenSSL.NET,我不想這樣做。

我在做什麼錯?

回答

8

您能否用ByteConverter.GetBytes替換爲Convert.FromBase64String並用Convert.ToBase64String替換ByteConverter.GetString並查看是否有幫助。 Bad Data異常通常表示數據中的字符無效,或者長度不正確,無法解密。我認爲使用Convert函數可能會解決您的問題。

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

嗯,試圖給我一個不同的例外:「System.FormatException:一個Base-64字符數組無效的長度」。這發生在'Encrypt'的第一行:byte [] bytesToEncrypt = Convert.FromBase64String(textToEncrypt);'。 – 2010-06-30 19:58:44

+1

@Sarah - 好的,我更新了你的例子。我測試了一下,它看起來很有效。 – SwDevMan81 2010-06-30 20:40:50

+0

這有效!謝謝。我從來沒有想過要使用'Convert' /'UnicodeEncoding'的混合。 – 2010-07-01 14:06:18

1

我會建議使用這個類,可惜我不記得原作者雖然..

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

    } 
} 
} 

用途爲:

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

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

編輯: 我還建議您不要將其用於大數據加密。通常你會用對稱算法(AES等)對實際數據進行加密,然後用RSA算法對對稱密鑰(隨機生成的)進行加密,然後發送rsa加密的對稱密鑰和對稱密鑰數據。 你也應該看看RSA簽名,以確保數據來自它說的地方..

3

你的問題是從字節到字符串的轉換。並非所有字節序列都是有效的UTF-16編碼,並且您正在使用默認忽略無效字節的UnicodeEncoding。如果您使用

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

而是試圖轉換爲字節與0xFFFD默默更換無效字節對,而不是當你的代碼就會失敗。

測試在調試期間工作的事實是一個巧合。您使用的是隨機RSA密鑰對,因此有時您將得到一種有效的UTF-16編碼加密。

正如SwDevMan81所建議的,修正是使用可以轉換所有可能的字節數組的編碼。 F.X.的Base64編碼。