2013-03-01 144 views
0

我有AES加密/解密的問題。評論的代碼工作,但有時給錯誤「填充是無效的,不能被刪除」,所以我改變它,因爲它在這裏解釋Padding is invalid and cannot be removed Exception while decrypting string using "AesManaged" C#Aes解密器給出空字符串

但是當我嘗試它下面的代碼在解密時給出一個空字符串。我不知道我在哪裏犯錯。兩個靜態函數bytesToString和stringToBytes與加密無關,我在其他地方使用它們。密鑰長度和塊大小是OKAY。 我發現這個在debbuger:

「 'csEncrypt.Length' 扔 'System.NotSupportedException' 類型的異常」

我在.NET 3.5的Visual Studio 2008

在這裏工作是調試prtscr如u可以在離開塊加密爲0字節長的CryptoStream有一些例外

Print screen from debugger

如何修復後看到了什麼?請給我一些線索。

static class Aes 
{ 
    public static string bytesToHexString(byte[] key) 
    { 
     return BitConverter.ToString(key).Replace("-", String.Empty); 
    } 

    public static byte[] stringToBytes(string key) 
    { 
     return Enumerable.Range(0, key.Length) 
       .Where(x => x % 2 == 0) 
       .Select(x => Convert.ToByte(key.Substring(x, 2), 16)) 
       .ToArray(); 
    } 

    public static void generateKeyAndIv(out byte[] key, out byte[] IV) 
    { 
     using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider()) 
     { 
      aesAlg.BlockSize = 128; 
      aesAlg.KeySize = 256; 
      aesAlg.Padding = PaddingMode.None; 
      //aesAlg.Mode = CipherMode.CBC; 
      aesAlg.GenerateKey(); 
      aesAlg.GenerateIV(); 
      key = aesAlg.Key; 
      IV = aesAlg.IV; 
     } 
    } 
    public static string EncryptStringToString(string plainText, byte[] Key, byte[] IV) 
    { 
     byte[] bytes =EncryptStringToBytes_Aes(plainText, Key, IV); 
     return Convert.ToBase64String(bytes); 
     //return Encoding.UTF8.GetString(bytes, 0, bytes.Length); 
    } 

    public static string DecryptStringToString(string cipherText, byte[] Key, byte[] IV) 
    { 
     //byte[] bytes = Encoding.UTF8.GetBytes(cipherText); 
     byte[] bytes = Convert.FromBase64String(cipherText); 
     return DecryptStringFromBytes_Aes(bytes, Key, IV); 
    } 

    public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV) 
    { 
     // Check arguments. 
     if (plainText == null || plainText.Length <= 0) 
      throw new ArgumentNullException("plainText"); 
     if (Key == null || Key.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     if (IV == null || IV.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     /*byte[] encrypted; 
     // Create an AesCryptoServiceProvider object 
     // with the specified key and IV. 
     using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider()) 
     { 
      aesAlg.BlockSize = 128; 
      aesAlg.KeySize = 256; 
      aesAlg.Padding = PaddingMode.PKCS7; 
      aesAlg.Mode = CipherMode.CBC; 
      aesAlg.Key = Key; 
      aesAlg.IV = IV; 
      // Create a decrytor to perform the stream transform. 
      ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); 


      // Create the streams used for encryption. 
      using (MemoryStream msEncrypt = new MemoryStream()) 
      { 
       using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
       { 
        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) 
        { 
         //Write all data to the stream. 
         swEncrypt.Write(plainText); 
        } 
       } 

       encrypted = msEncrypt.ToArray(); 
      } 
     }*/ 
     byte[] encrypted; 
     // Create an AesManaged object 
     // with the specified key and IV. 
     using (AesManaged aesAlg = new AesManaged()) 
     { 

      // Create a decrytor to perform the stream transform. 
      aesAlg.Padding = PaddingMode.None; 
      aesAlg.BlockSize = 128; 
      aesAlg.KeySize = 256; 
      ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); 

      // Create the streams used for encryption. 
      using (var msEncrypt = new MemoryStream()) 
      using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
      using (var swEncrypt = new StreamWriter(csEncrypt)) 
      { 
       swEncrypt.Write(plainText); 
       csEncrypt.FlushFinalBlock(); 
       encrypted = msEncrypt.ToArray(); 
      } 
     } 

     //return encrypted; 


     // Return the encrypted bytes from the memory stream. 
     return encrypted; 

    } 

    public static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV) 
    { 
     // Check arguments. 
     if (cipherText == null || cipherText.Length <= 0) 
      throw new ArgumentNullException("cipherText"); 
     if (Key == null || Key.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     if (IV == null || IV.Length <= 0) 
      throw new ArgumentNullException("IV"); 

     // Declare the string used to hold 
     // the decrypted text. 
     string plaintext = null; 

     // Create an AesCryptoServiceProvider object 
     // with the specified key and IV. 
     using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider()) 
     { 
      aesAlg.BlockSize = 128; 
      aesAlg.KeySize = 256; 
      aesAlg.Padding = PaddingMode.PKCS7; 
      aesAlg.Mode = CipherMode.CBC; 
      aesAlg.Key = Key; 
      aesAlg.IV = IV; 
      // Create a decrytor to perform the stream transform. 
      ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); 

      // Create the streams used for decryption. 
      using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 
      { 
       using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
       { 
        using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
        { 

         // Read the decrypted bytes from the decrypting stream 
         // and place them in a string. 
         plaintext = srDecrypt.ReadToEnd(); 
        } 
       } 
      } 

     } 

     return plaintext; 

    } 
} 

好的,也許我會解釋一下整個情況。我寫了自己的安全電子郵件。我有兩次加密。服務器和客戶端之間的通信由RSA和AES加密。用戶創建的消息也由RSA + AES加密。

發送的消息是這樣的:

  1. 客戶端連接到服務器。
  2. 建立安全連接(服務器發送公鑰,客戶端生成AES密鑰,通過服務器的公鑰加密後發送給服務器,服務器和客戶端使用AES密鑰進行通信後)。
  3. 客戶端使用XML創建消息,消息可以包含讀取到base64的文件,之後使用AES加密。
  4. 該消息被寫入數據庫。

recieving消息是這樣的:

  1. 連接到服務器。
  2. 建立安全連接。
  3. 從服務器獲取消息。
  4. 使用RSA私鑰解密AES密鑰。
  5. 使用解密的AES密鑰解密消息。
  6. 如果有文件,然後使用AES解密它們,並將它們解碼爲字節並保存。

現在的問題是加密大數據。即使200-300 kB也有問題。

我發現的另一個有趣的事情是,當我通過調試器運行代碼時,它可以工作,但是當我沒有它運行代碼時。它不起作用。


解決方案

我發現的問題的解決方案。因爲我使用不同的密鑰/ iv來非常快速地使用AES加密/解密兩次垃圾收集器沒有清除這些對象。剛剛返回由DecryptStringFromBytes_Aes和EncryptStringToBytes_Aes

我希望這會幫助誰是有同樣的問題,就像我有某人前值溶液中加入

GC.Collect(); 
GC.WaitForPendingFinalizers(); 

+1

它會爲你,如果你整理您的實現一點發現問題更容易。 – 2013-03-01 08:32:11

+0

現在我試圖從這裏使用http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aspx代碼,並且此代碼給我「填充無效」異常。因此,當我使用來自MSDN的示例代碼時,我有一個空字符串或有時會出現「填充無效異常」。 – Robert 2013-03-01 08:34:14

+0

我發現msdn中的代碼在需要大量數據解密/加密的情況下無法使用。 – Robert 2013-03-01 08:52:35

回答

0

你使用流的方式意味着沒有數據被寫入底層流,我不知道爲什麼,但我確實找到了一種我已經重寫的替代方法。

使用下面的代碼,您應該能夠作爲控制檯應用程序運行,然後重新塑造它,使其適用於您的目的。讓我知道如何去:

using System; 
using System.IO; 
using System.Security.Cryptography; 
using System.Text; 

namespace Encrypto 
{ 
    public static class Program 
    { 
     public static void Main() 
     { 

      const string password = "test"; 
      const string text = "test"; 
      var cipherText = Aes.Encrypt(password, text); 
      var decrypted = Aes.Decrypt(password, cipherText); 

      Console.WriteLine(decrypted); 
      Console.ReadKey(); 
     } 
    } 

    internal static class Aes 
    { 
     public static EncryptedData Encrypt(string password, string data) 
     { 
      return Transform(true, password, data, null) as EncryptedData; 
     } 

     public static string Decrypt(string password, EncryptedData data) 
     { 
      return Transform(false, password, data.DataString, data.SaltString) as string; 
     } 

     private static object Transform(bool encrypt, string password, string data, string saltString) 
     { 
      using (var aes = new AesManaged()) 
      { 
       aes.Mode = CipherMode.CBC; 
       aes.Padding = PaddingMode.PKCS7; 
       var keyLen = aes.KeySize/8; 
       var ivLen = aes.BlockSize/8; 
       const int saltSize = 8; 
       const int iterations = 8192; 

       var salt = encrypt ? new byte[saltSize] : Convert.FromBase64String(saltString); 
       if (encrypt) 
       { 
        new RNGCryptoServiceProvider().GetBytes(salt); 
       } 

       var bcKey = new Rfc2898DeriveBytes("BLK" + password, salt, iterations).GetBytes(keyLen); 
       var iv = new Rfc2898DeriveBytes("IV" + password, salt, iterations).GetBytes(ivLen); 
       var macKey = new Rfc2898DeriveBytes("MAC" + password, salt, iterations).GetBytes(16); 

       aes.Key = bcKey; 
       aes.IV = iv; 

       var rawData = encrypt ? Encoding.UTF8.GetBytes(data) : Convert.FromBase64String(data); 

       using (var transform = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor()) 
       using (var memoryStream = encrypt ? new MemoryStream() : new MemoryStream(rawData)) 
       using (var cryptoStream = new CryptoStream(memoryStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read)) 
       { 
        if (encrypt) 
        { 
         cryptoStream.Write(rawData, 0, rawData.Length); 
         cryptoStream.FlushFinalBlock(); 
         return new EncryptedData(salt, macKey, memoryStream.ToArray()); 
        } 
        var originalData = new byte[rawData.Length]; 
        var count = cryptoStream.Read(originalData, 0, originalData.Length); 

        return Encoding.UTF8.GetString(originalData, 0, count); 
       } 
      } 
     } 

     public class EncryptedData 
     { 
      public EncryptedData(byte[] salt, byte[] mac, byte[] data) 
      { 
       Salt = salt; 
       MAC = mac; 
       Data = data; 
      } 

      private byte[] Salt { get; set; } 

      public string SaltString 
      { 
       get { return Convert.ToBase64String(Salt); } 
      } 

      private byte[] MAC { get; set; } 

      private byte[] Data { get; set; } 

      public string DataString 
      { 
       get { return Convert.ToBase64String(Data); } 
      } 
     } 
    } 
} 

來源提供這樣的回答:Using AES encryption in .NET - CryptographicException saying the padding is invalid and cannot be removed

+0

這是一樣的。我在第一篇文章中詳細解釋了我的全部問題。我不記得爲什麼我有問題,我使用MSDN的例子。 – Robert 2013-03-01 10:26:41

+0

好吧,您從MSDN嘗試的代碼並不適合您,它適合我,因此您需要仔細觀察您擁有的內容並嘗試其他方法。你是否運行了我在新的控制檯項目中鏈接的代碼,或者它是否在您的現有應用程序中? – 2013-03-01 10:43:16

+0

您沒有使用與示例相同的代碼,不同的位可能會導致您的問題,例如'generateKeyAndIv()' – 2013-03-01 10:48:01