2012-03-08 5 views
4

Использование класса PasswordDeriveBytes в используемом блоке (который его использует, поскольку оно реализует IDisposable) создает проблему, если класс используется во второй раз. Это код:PasswordDeriveBytes (System.Security.Cryptography) терпит неудачу, если установлено

public class AES 
{ 
    protected static CryptoData localCryptoData; 

    static AES() 
    { 
     localCryptoData = new CryptoData(); 
    } 

    public static string Encrypt(CryptoData cryptoData) 
    { 
     using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password, cryptoData.Salt, "SHA1", 2)) 
     using (RijndaelManaged symmetricKey = new RijndaelManaged()) 
     { 
      byte[] keyBytes = pass.GetBytes(cryptoData.KeySize/8); 
      symmetricKey.Padding = PaddingMode.PKCS7; 
      symmetricKey.Mode = CipherMode.CBC; 

      using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, cryptoData.InitVector)) 
      using (MemoryStream memoryStream = new MemoryStream()) 
      using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) 
      { 
       cryptoStream.Write(cryptoData.ByteText, 0, cryptoData.ByteText.Length); 
       cryptoStream.FlushFinalBlock(); 
       return Convert.ToBase64String(memoryStream.ToArray()); 
      } 
     } 
    } 

    public static string Decrypt(CryptoData cryptoData) 
    { 
     using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password, cryptoData.Salt, "SHA1", 2)) 
     using (RijndaelManaged symmetricKey = new RijndaelManaged()) 
     { 
      byte[] cipherTextBytes = Convert.FromBase64String(cryptoData.Text); 
      byte[] keyBytes = pass.GetBytes(cryptoData.KeySize/8); 
      symmetricKey.Padding = PaddingMode.PKCS7; 
      symmetricKey.Mode = CipherMode.CBC; 

      using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, cryptoData.InitVector)) 
      using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes)) 
      using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) 
      { 
       byte[] textBytes = new byte[cipherTextBytes.Length]; 
       int count = cryptoStream.Read(textBytes, 0, textBytes.Length); //throws CryptographicException - Padding is invalid and cannot be removed. 
       return Encoding.UTF8.GetString(textBytes, 0, count); 
      } 
     } 
    } 

Если этот класс используется таким образом:

AES.Encrypt (cryptoData); AES.Decrypt (cryptoData);

Первое использование дает вам правильную зашифрованную строку AES, но если сбой исключается при попытке расшифровки одной и той же строки. Проблема заключается в назначении первого параметра (пароль для вывода ключа) из класса PasswordDeriveBytes, когда этот пароль задается через массив байтов. Если он задан как строка (из-за перегрузки), он работает нормально.

Помощник CryptoData Класс:

public class CryptoData 
{ 
    private string text; 
    public string Text 
    { 
     get { return text; } 
     set 
     { 
      text = value; 

      if (value != null) 
      { 
       ByteText = Encoding.ASCII.GetBytes(value); 
      } 
      else 
      { 
       ByteText = null; 
      } 
     } 
    } 

    public byte[] ByteText { get; private set; } 
    public byte[] Password { get; set; } 
    public int KeySize { get; set; } 
    public byte[] InitVector { get; set; } 
    public byte[] Salt { get; set; } 
} 

Если вы просто изменить эту строку в методах:

using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password, 
     cryptoData.Salt, "SHA1", 2)) 

в

using (PasswordDeriveBytes pass = new PasswordDeriveBytes("somePassword", 
     cryptoData.Salt, "SHA1", 2)) 

все работает отлично. Проблема в том, что экземпляр PasswordDeriveBytes не получает байтовый массив для пароля, который используется во второй раз из-за оператора using. Если строка была передана, то вместо байтового массива она работает.

Редактирование: после просмотра его ближе, кажется, что в настройке свойства по умолчанию для параметра пароля существует проблема. Он получает указатель на массив, и именно поэтому он распоряжается им. Он должен сделать value.clone() массива, как в случае с массивом солей. Это определенная ошибка.

Я прав, или я делаю что-то неправильно?

Edit:

* Изменение первая строка в AES.Encrypt() и AES.Decrypt методы с этим и работает: *

using (PasswordDeriveBytes pass = new PasswordDeriveBytes(
     (byte[])cryptoData.Password.Clone(), 
     cryptoData.Salt, "SHA1", 2)) 
+0

Вы не можете использовать объект после его удаления. –

+2

Пожалуйста, прочитайте и поймите код. Никто не использует один и тот же объект дважды. Существуют разные методы. – mileski

+2

Можете ли вы опубликовать данные об исключениях, включая трассировку стека? –

ответ

1

Это, безусловно, нелогичным и недокументированное поведение, хотя может быть обсуждено или нет это ошибка. В основном, когда вы передаете массив байтов пароля в конструктор, экземпляр PasswordDeriveBytes берет на себя ответственность за этот массив. Это похоже на то, как StreamReader получает право собственности на Stream, который передается ему, и будет Dispose, когда он удален (это поведение также было criticized по аналогичным причинам, что привело к добавлению булевского параметра в конструктор StreamReader в .NET 4.0, который может помешать базовому потоку быть удаленным).

Клонирование массива байтов, прежде чем передавать его, вероятно, является вашим лучшим вариантом.

Смежные вопросы