2013-03-01 2 views
0

У меня проблема с AES-шифрованием/расшифровкой. Прокомментированный код работал, но иногда давал ошибку «заполнение недействительно и не может быть удалено», поэтому я изменил его, как описано здесь Padding is invalid and cannot be removed Exception while decrypting string using "AesManaged" C#Aes decryptor дает пустую строку

, но когда я его попробовал, код ниже при расшифровке дает пустую строку. Я не знаю, где я ошибаюсь. Две статические функции bytesToString и stringToBytes не имеют никакого отношения к шифрованию, и я использую их в другом месте. Длина ключа и размер блока - OKAY. Я нашел это в debbuger:

" 'csEncrypt.Length' бросил исключение типа" System.NotSupportedException"

Работаю на 3.5 .NET Visual Studio 2008

здесь PrtScr из отладчика а можете видеть после чего блок шифруется 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; 

    } 
} 

ОК, возможно, я объясню всю ситуацию немного больше. Я написал собственное безопасное электронное письмо. У меня 2x шифрование. Связь между сервером и клиентом шифруется RSA и AES. Также сообщения, созданные пользователями, зашифровываются RSA + AES.

посылая сообщение выглядит следующим образом:

  1. клиент подключается к серверу.
  2. Они устанавливают безопасное соединение (сервер отправляет свой открытый ключ, клиент генерирует ключ AES, шифрует его через открытый ключ сервера и отправляет его на сервер. После этого сервер и клиент используют ключ AES для связи).
  3. Клиент создает сообщение в XML, сообщение может содержать файлы, которые считываются на base64, и после этого они зашифровываются с помощью AES.
  4. Сообщение написано на db.

ПОЛУЧАТЬ сообщение выглядит следующим образом:

  1. Подключение к серверу.
  2. Установить безопасное соединение.
  3. Получить сообщения с сервера.
  4. Расшифровать ключ AES с использованием секретного ключа RSA.
  5. Расшифровка сообщения с использованием дешифрованного ключа AES.
  6. Если есть файлы, расшифруйте их с помощью AES и base64_decode в байтах и ​​сохраните.

Теперь проблема заключается в шифровании больших данных. Иногда даже 200-300 кБ.

Еще одна интересная вещь, которую я обнаружил, заключается в том, что когда я запускаю код через отладчик, он работает, но когда я запускаю код без него. Это не работает.


Решение

Я нашел решение проблемы. Поскольку я использовал шифрование/дешифрование AES дважды очень быстро друг за другом с разными ключами/ivs, сборщик мусора не очищал эти объекты. Раствор добавления

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

как раз перед возвращением значения по DecryptStringFromBytes_Aes и EncryptStringToBytes_Aes

Я надеюсь, что это поможет кому-то, кто, имеющий такой же вопрос, как и я.

+1

Это будет проще для вас найти проблему, если вы прибрать вашу реализацию немного. –

+0

Теперь я попытался использовать http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aspx код отсюда, и этот код дает мне исключение «padding invalid». Таким образом, либо я имею пустую строку, либо иногда «заполнение недопустимого исключения», когда я использую образец кода из MSDN. – Robert

+0

Я узнал, что код из msdn не работает там, где есть большой объем данных для дешифрования/шифрования. – Robert

ответ

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

Sources для обеспечения такого ответа: Using AES encryption in .NET - CryptographicException saying the padding is invalid and cannot be removed

+0

Это то же самое. Я подробно объяснил всю мою проблему в первом посте. Я не понимаю, почему у меня проблема, я использую этот пример из MSDN. – Robert

+0

Хорошо, код, который вы пробовали из MSDN, не работает для вас, это для меня, поэтому вам нужно подойти поближе к тому, что у вас есть, и, возможно, попробовать что-то еще. Вы запустили код, который я связал в новом проекте консоли, или он находится в вашем существующем приложении? –

+0

Вы не используете код так же, как в примере, разные биты - это то, что может вызвать проблемы, например 'generateKeyAndIv()' –