PasswordDeriveBytes (System.Security.Cryptography) fails if

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); //throws CryptographicException - Padding is invalid and cannot be removed. return Encoding.UTF8.GetString(textBytes, 0, count); } } } 

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)) 
+4
source share
1 answer

This is certainly counterintuitive and undocumented behavior, although is it possible to discuss this error. Basically, when you pass an array of password bytes to the constructor, an instance of PasswordDeriveBytes takes responsibility for this array. This is similar to how StreamReader gains ownership of the Stream that is transferred to it and will Dispose when it is deleted (this behavior has also been criticized on a similar basis, which led to the addition of a Boolean parameter to the StreamReader constructor in .NET 4.0, which can prevent the underlying thread from being Disposed).

Cloning an array of bytes before passing it is likely to be your best option.

+1
source

Source: https://habr.com/ru/post/1400339/


All Articles