2010-07-29 2 views
3

Я создал класс для шифрования и дешифрования с использованием AES..NET AES/Rijndael - несогласованное дешифрование при повторном использовании decryptor

public class AesEncryptionProvider { 
    #region Fields 

    // Encryption key 
    private static readonly byte[] s_key = new byte[32] { 
     // Omitted... 
    }; 

    // Initialization vector 
    private static readonly byte[] s_iv = new byte[16] { 
     // Omitted... 
    }; 

    private AesCryptoServiceProvider m_provider; 
    private ICryptoTransform m_encryptor; 
    private ICryptoTransform m_decryptor; 

    #endregion 

    #region Constructors 

    private AesEncryptionProvider() { 
     m_provider = new AesCryptoServiceProvider(); 
     m_encryptor = m_provider.CreateEncryptor(s_key, s_iv); 
     m_decryptor = m_provider.CreateDecryptor(s_key, s_iv); 
    } 

    static AesEncryptionProvider() { 
     Instance = new AesEncryptionProvider(); 
    } 

    #endregion 

    #region Properties 

    public static AesEncryptionProvider Instance { get; private set; } 

    #endregion 

    #region Methods 

    public string Encrypt (string value) { 
     if (string.IsNullOrEmpty(value)) { 
      throw new ArgumentException("Value required."); 
     } 

     return Convert.ToBase64String(
      Transform(
       Encoding.UTF8.GetBytes(value), 
       m_encryptor)); 
    } 

    public string Decrypt (string value) { 
     if (string.IsNullOrEmpty(value)) { 
      throw new ArgumentException("Value required."); 
     } 

     return Encoding.UTF8.GetString(
      Transform(
       Convert.FromBase64String(value), 
       m_decryptor)); 
    } 

    #endregion 

    #region Private methods 

    private byte[] Transform (byte[] input, ICryptoTransform transform) { 
     byte[] output; 
     using (MemoryStream memory = new MemoryStream()) { 
      using (CryptoStream crypto = new CryptoStream(
       memory, 
       transform, 
       CryptoStreamMode.Write 
      )) { 
       crypto.Write(input, 0, input.Length); 
       crypto.FlushFinalBlock(); 

       output = memory.ToArray(); 
      } 
     } 
     return output; 
    } 

    #endregion 
} 

Как вы можете видеть, в обоих случаях я писать к MemoryStream через CryptoStream. Если я создаю новый дешифратор с помощью m_provider.CreateDecyptor(s_key, s_iv) при каждом вызове Decrypt, он работает нормально.

Что пошло не так? Почему дешифратор ведет себя так, как будто его забыл IV? Есть ли что-то, что делает вызов StreamReader.ReadToEnd(), который помогает m_decryptor правильно функционировать?

Я хотел бы избежать любого из двух «рабочих» подходов, которые перечислены здесь, так как есть удар по производительности для обоих, и это очень важный путь. Заранее спасибо.

+1

Пожалуйста, разместите ** настоящий ** код, который шифрует и дешифрует. Подход не похож, что это проблематично. –

+0

Обновлено с полным классом. – iamtyler

ответ

1

Хорошо, я не знаю, почему это работает, но смените AesCryptoServiceProvider на AesManaged и вуаля.

Я также рекомендую сделать свой класс реализуемым IDisposable, поскольку он содержит три переменные-члены, которые ее реализуют. См. Ниже для изменения кода:

public sealed class AesEncryptionProvider : IDisposable 
{ 
    // Encryption key 
    private static readonly byte[] key = new byte[] 
    { 
     // Omitted... 
    }; 

    // Initialization vector 
    private static readonly byte[] iv = new byte[] 
    { 
     // Omitted... 
    }; 

    private static readonly AesEncryptionProvider instance = new AesEncryptionProvider(); 

    private readonly AesManaged provider; 

    private readonly ICryptoTransform encryptor; 

    private readonly ICryptoTransform decryptor; 

    private AesEncryptionProvider() 
    { 
     this.provider = new AesManaged(); 
     this.encryptor = this.provider.CreateEncryptor(key, iv); 
     this.decryptor = this.provider.CreateDecryptor(key, iv); 
    } 

    public static AesEncryptionProvider Instance 
    { 
     get 
     { 
      return instance; 
     } 
    } 

    public void Dispose() 
    { 
     this.decryptor.Dispose(); 
     this.encryptor.Dispose(); 
     this.provider.Dispose(); 
     GC.SuppressFinalize(this); 
    } 

    public string Encrypt(string value) 
    { 
     if (string.IsNullOrEmpty(value)) 
     { 
      throw new ArgumentException("Value required."); 
     } 

     return Convert.ToBase64String(Transform(Encoding.UTF8.GetBytes(value), this.encryptor)); 
    } 

    public string Decrypt(string value) 
    { 
     if (string.IsNullOrEmpty(value)) 
     { 
      throw new ArgumentException("Value required."); 
     } 

     return Encoding.UTF8.GetString(Transform(Convert.FromBase64String(value), this.decryptor)); 
    } 

    private static byte[] Transform(byte[] input, ICryptoTransform transform) 
    { 
     using (var memory = new MemoryStream()) 
     using (var crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write)) 
     { 
      crypto.Write(input, 0, input.Length); 
      crypto.FlushFinalBlock(); 
      return memory.ToArray(); 
     } 
    } 
} 
+0

Я как раз собирался приходить на почту то же самое! Он работает либо с RijndaelManaged, как вы сказали, либо с AesManaged. Я нашел некоторую информацию о различиях между этими тремя (http://stackoverflow.com/questions/1228451/when-would-i-choose-aescryptoserviceprovider-over-aesmanaged-or-rijndaelmanaged), и я выбрал AesManaged. Довольно загадка, но, по крайней мере, она работает! Благодаря! – iamtyler

+0

Только проблема с использованием алгоритмов управляемого управления не существует [FIPS] (http://en.wikipedia.org/wiki/FIPS_140). Пришел здесь к одному и тому же вопросу, но мне нужно, чтобы мои предположения соответствовали требованиям FIPS. – Darren

+0

@iamtyler, вы также можете повторно использовать экземпляр AesCryptoServiceProvider и просто создавать новые шифры и расшифровки для каждого вызова. Это сработало для меня. –

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