Another year, another change to the way the good folks at Microsoft want us to write encryption/decryption code. Now (Feb. 2022, .Net 6) we are supposed to use the new Aes class instead of the deprecated RijndaelManaged class and its cousins.
So, here is my symmetrical encryption/decryption code:
/// <summary> Encrypts a byte array using the AES encryption algorithm. </summary>
/// <param name="clearData"> The data to encrypt. </param>
/// <param name="key"> The encryption key. </param>
/// <param name="iv"> The initialization vector. </param>
/// <returns> The encrypted data. </returns>
public static byte[] Encrypt(byte[] clearData, byte[] key, byte[] iv)
{
Contract.Requires(clearData != null);
Contract.Requires(key != null);
Contract.Requires(iv != null);
using (var aes = Aes.Create()) {
aes.Key = key;
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) {
return _PerformCryptography(clearData, encryptor);
}
}
}
/// <summary> Decrypts a byte array using the AES encryption algorithm. </summary>
/// <param name="encryptedData"> The encrypted data. </param>
/// <param name="key"> The encryption key. </param>
/// <param name="iv"> The initialization vector. </param>
/// <returns> The decrypted data. </returns>
public static byte[] Decrypt(byte[] encryptedData, byte[] key, byte[] iv)
{
Contract.Requires(encryptedData != null);
Contract.Requires(key != null);
Contract.Requires(iv != null);
using (var aes = Aes.Create()) {
aes.Key = key;
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) {
return _PerformCryptography(encryptedData, decryptor);
}
}
}
/// <summary> Performs symmetrical encryption or decryption using a specified transformation algorithm. </summary>
/// <param name="data"> The data to encrypt or decrypt. </param>
/// <param name="cryptoTransform"> The crypto algorithm. </param>
/// <returns> The encrypted or decrypted data. </returns>
private static byte[] _PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
{
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write);
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
My specific question has to do with the using
statements in _PerformCryptography
. Rather than relying on using
statements to dispose objects, my previous implementation used a try
/finally
construct to avoid disposing the MemoryStream
twice:
// Get a SymmetricAlgorithm object
using var algorithm = new RijndaelManaged();
// Set the key and the initialization vector
algorithm.Key = key;
algorithm.GenerateIV();
// Encrypt the information to a MemoryStream
MemoryStream ms = null;
CryptoStream cs = null;
try {
// See comment below re: "multiple dispose" issue
ms = new MemoryStream();
// Encrypt the data
cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearBytes, 0, clearBytes.Length);
cs.FlushFinalBlock();
// Return the encrypted stream of data as a byte array
return ms.ToArray();
}
finally {
if (cs != null) {
// Dispose the CryptoStream, which disposes the MemoryStream that it contains
ms = null;
cs.Dispose();
}
if (ms != null) ms.Dispose();
}
Is this still required, or has something changed so the "multiple dispose" is no longer an issue? (Or maybe it was never really an issue to begin with?)