2010-07-29 25 views
3

J'ai créé une classe pour le cryptage et le décryptage à l'aide d'AES..NET AES/Rijndael - décryptage incohérent lors de la réutilisation du décrypteur

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 
} 

Comme vous pouvez le voir, dans les deux cas, je suis écrit à un MemoryStream via un CryptoStream. Si je crée un nouveau décrypteur via m_provider.CreateDecyptor(s_key, s_iv) à chaque appel à Decrypt cela fonctionne très bien.

Qu'est-ce qui a mal tourné ici? Pourquoi le décrypteur se comporte-t-il comme si son IV était oublié? Y at-il quelque chose que l'appel à StreamReader.ReadToEnd() fait qui aide m_decryptor fonctionne correctement?

Je voudrais éviter l'une ou l'autre des deux approches «fonctionnantes» que j'ai énumérées ici, car il y a un coup de performance sur les deux et c'est un chemin très critique. Merci d'avance.

+1

Veuillez écrire le code ** réel ** qui est en train de chiffrer et de déchiffrer. L'approche ne semble pas être problématique. –

+0

Mis à jour avec la classe complète. – iamtyler

Répondre

1

Ok, j'avoue que je n'ai aucune idée pourquoi cela fonctionne, mais change AesCryptoServiceProvider à AesManaged et le tour est joué.

Je recommande également de mettre en œuvre votre classe IDisposable car elle contient trois variables membres qui l'implémentent. Voir ci-dessous pour les changements de code:

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

J'étais sur le point de venir poster la même chose! Cela fonctionne avec RijndaelManaged, comme vous l'avez dit, ou AesManaged. J'ai trouvé quelques informations sur les différences entre les trois (http://stackoverflow.com/questions/1228451/when-would-i-choose-aescryptoserviceprovider-over-aesmanaged-or-rijndaelmanaged), et je choisis AesManaged. Tout un mystère, mais au moins ça marche! Merci! – iamtyler

+0

Seul le problème avec l'utilisation des algorithmes de chiffrement gérés n'est pas compatible [FIPS] (http://en.wikipedia.org/wiki/FIPS_140). Entré ici avec la même question, mais j'ai besoin de mon implimentation pour être conforme à la norme FIPS. – Darren

+0

@iamtyler, vous pouvez également réutiliser l'instance AesCryptoServiceProvider et créer simplement de nouveaux chiffreurs et décrypteurs pour chaque appel. Cela a fonctionné pour moi. –