Using the PasswordDeriveBytes class in a usage block (which uses it because it implements IDisposable) poses a problem if the class is used a second time. This is the code:
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);
If this class is used this way:
AES.Encrypt (cryptoData); AES.Decrypt (cryptoData);
The first use gives you the correct AES encrypted string, but if a failure is thrown when you try to decrypt the same string. The problem is the assignment of the first parameter (password to display the key) from the PasswordDeriveBytes class when this password is specified through an array of bytes. If it is set as a string (due to overload), it works fine.
Helper class 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; } }
If you just change this line in the methods:
using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password, cryptoData.Salt, "SHA1", 2))
in
using (PasswordDeriveBytes pass = new PasswordDeriveBytes("somePassword", cryptoData.Salt, "SHA1", 2))
everything is working fine. The problem is that the PasswordDeriveBytes instance does not receive a byte array for the password, which is used a second time due to the using statement. If the string was passed, then instead of the byte array, it works.
Edit: After a more detailed look, it seems that there is a problem in setting the default properties for the password parameter. It gets a pointer to an array, and that is why it uses it. It should make the value.clone () of the array, as is the case with the salt array. This is a definite mistake.
Am I right or am I doing something wrong?
Edit:
* Change the first line in AES.Encrypt () and AES.Decrypt using this method: *
using (PasswordDeriveBytes pass = new PasswordDeriveBytes( (byte[])cryptoData.Password.Clone(), cryptoData.Salt, "SHA1", 2))