I'm observing a difference in the output of AES encryption when using System.Security.Cryptography.CryptoStream depending on how I get the plaintext bytes into the CryptoStream, but I don't understand why the differences occur.
private Aes CreateAes()
{
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = ...;
aes.IV = ...;
return aes;
}
var plaintext = "This is some plaintext. Isn't it nice?";
var plainbytes = Encoding.UTF8.GetBytes(plaintext);
// Method 1: writing a string with a `StreamWriter`:
using (var aes = CreateAes())
{
using (var output = new MemoryStream())
using (var encryptor = aes.CreateEncryptor())
{
using (var encryptingStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
using (var writer = new StreamWriter(encryptingStream))
{
writer.Write(plaintext);
encryptingStream.FlushFinalBlock();
}
var ciphertext = output.ToArray();
Console.WriteLine("Method 1: {0}", BitConverter.ToString(ciphertext));
}
}
// Method 2: writing bytes directly to the `CryptoStream`:
using (var aes = CreateAes())
{
using (var output = new MemoryStream())
using (var encryptor = aes.CreateEncryptor())
{
using (var encryptingStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
encryptingStream.Write(plainbytes, 0, plainbytes.Length);
encryptingStream.FlushFinalBlock();
}
var ciphertext = output.ToArray();
Console.WriteLine("Method 2: {0}", BitConverter.ToString(ciphertext));
}
}
// Method 3: copying a written input stream
using (var aes = CreateAes())
{
using (var input = new MemoryStream())
using (var writer = new StreamWriter(input))
{
writer.Write(plaintext);
using (var output = new MemoryStream())
using (var encryptor = aes.CreateEncryptor())
{
using (var encryptingStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
input.CopyTo(encryptingStream);
encryptingStream.FlushFinalBlock();
}
var ciphertext = output.ToArray();
Console.WriteLine("Method 3: {0}", BitConverter.ToString(ciphertext));
}
}
}
// Method 4: Copying a byte-wrapper stream:
using (var aes = CreateAes())
{
using (var input = new MemoryStream(plainbytes, writable: false))
using (var output = new MemoryStream())
using (var encryptor = aes.CreateEncryptor())
{
using (var encryptingStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
input.CopyTo(encryptingStream);
encryptingStream.FlushFinalBlock();
}
var ciphertext = output.ToArray();
Console.WriteLine("Method 4: {0}", BitConverter.ToString(ciphertext));
}
}
When I execute these, I get the following:
Method 1: 4F-5A-EA-93-53-C4-6E-2B-7B-C1-DE-EF-C3-5E-37-9A-A1-94-FA-25-A8-B7-3A-F2-8F-5D-69-74-4E-BD-B3-C6-A9-B8-B7-37-7A-8D-20-B1-3E-9E-8C-D4-EE-F1-EC-BB`
Method 2: A1-94-FA-25-A8-B7-3A-F2-8F-5D-69-74-4E-BD-B3-C6-A9-B8-B7-37-7A-8D-20-B1-3E-9E-8C-D4-EE-F1-EC-BB-F2-C9-39-B4-63-68-9B-0F-FF-75-80-4D-FC-18-5B-09
Method 3: 4F-5A-EA-93-53-C4-6E-2B-7B-C1-DE-EF-C3-5E-37-9A
Method 4: A1-94-FA-25-A8-B7-3A-F2-8F-5D-69-74-4E-BD-B3-C6-A9-B8-B7-37-7A-8D-20-B1-3E-9E-8C-D4-EE-F1-EC-BB-F2-C9-39-B4-63-68-9B-0F-FF-75-80-4D-FC-18-5B-09
I note that methods 2 and 4 produce identical output, while method 3's output is a truncation of method 1.
My understanding is that StreamWriter uses UTF-8 encoding by default, so why am I seeing such differences? And which is actually correct?
ToArray()is essentially designed to be used on the disposed MemoryStream to grab underlying bytes (in some cases you can cheat with manualClose/FlushFinalBlockcalls... but you need to pay far more care for that to be useful as minimal reproducible example).input.CopyTo(encryptingStream);you forgot to rewind the stream firstinput.Position = 0;and also to flush thewriter.