For testing I want to configure any .Net AES algorithm to produce the results given in the FIPS 197 publication (Appendix B).
I tried different parameters (block size = key size = feedback size = 128, different CipherModes and paddings, IV, ...) but i can't get the results given as example. I have also tried the different implementations (RijnadaelManaged, AesCryptoServiceProvider, ...).
Is there a way to imitate the given example?
This is my current implementation:
var data = new byte[] { 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 };
var key = new byte[] { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
var expected = new byte[] { 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 };
using (var aesAlg = new System.Security.Cryptography.RijndaelManaged())
{
aesAlg.Key = key;
aesAlg.IV = key;
aesAlg.KeySize = 128;
aesAlg.Mode = System.Security.Cryptography.CipherMode.ECB;
aesAlg.BlockSize = 128;
aesAlg.FeedbackSize = 128;
var encryptor = aesAlg.CreateEncryptor();
using (MemoryStream msEncrypt = new MemoryStream())
{
using (var csEncrypt = new System.Security.Cryptography.CryptoStream(msEncrypt, encryptor, System.Security.Cryptography.CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(data);
}
var actual = msEncrypt.ToArray();
Assert.AreEqual(expected.GetLength(0), actual.GetLength(0));
for (int i = 0; i < actual.GetLength(0); i++)
{
var expectedValue = expected[i];
var actualValue = actual[i];
Assert.AreEqual(expectedValue, actualValue);
}
}
}
}
ECB is not entirely the same as the block cipher itself. It is a mode of operation just like CBC or CTR, but it is more primitive and insecure. Instead of encrypting a single block of plaintext it manages to encrypt multiple blocks, although each of those blocks are simply encrypted directly.
To make sure that it confirms to a generic cipher that supports any message length (up to a certain size), ECB requires padding. Otherwise messages that are not a multiple of the block size cannot be encrypted. By default RijndaelManaged
will pad with PKCS#7 compatible padding. This padding is always applied because otherwise the message length cannot be determined (a message could end with the correct padding bytes "by accident"). So what you get when you're encrypting is a block consisting of the expected bytes, followed by a block of encrypted padding bytes.
To avoid this you can still use the ECB implementation, but then set the Padding
property of the class to PaddingMode.None
(for more padding modes look here). You could also strip the final block of padding before validating the calculated ciphertext or simply ignore the length check but I'd consider that hacking.
Here is code that works as intended:
using System;
using System.IO;
using System.Security.Cryptography;
namespace AESEncryption
{
class Program
{
static void Main(string[] args)
{
var data = new byte[] { 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 };
var key = new byte[] { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
var expected = new byte[] { 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 };
using (var aesAlg = new RijndaelManaged())
{
aesAlg.Key = key;
aesAlg.Mode = CipherMode.ECB;
aesAlg.Padding = PaddingMode.None;
var encryptor = aesAlg.CreateEncryptor();
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(data, 0, data.Length);
}
var actual = msEncrypt.ToArray();
Console.WriteLine("Expected:");
PrintByteArray(expected);
Console.WriteLine("Actual:");
PrintByteArray(actual);
}
}
}
static void PrintByteArray(byte[] array)
{
foreach (var b in array)
{
Console.Write($"{b:X2} ");
}
Console.WriteLine();
}
}
}
Note the following:
KeySize
after setting the key regenerates the key;FeedbackSize
is only applicable to OFB/CFB mode so that property should not be set (although it seems to be ignored);CryptoStream
already provides a binary stream to write to, do not use StreamWriter
for binary streams.Note: you should not use RijndaelManaged
directly. Instead use Aes.Create()
to get the best implementation of AES on your system. Usually that uses AES-NI or similar CPU features for a more secure and faster AES.
From the documentation:
The
Rijndael
andRijndaelManaged
types are obsolete. UseAes
instead.