Encrypt and Decrypt with AES

Updated 13/02/2025

My team needed a simple way to encrypt and decrypt some data, We chose Advanced Encryption Standard (AES), being a symmetric algorithm, it would use the same key for encryption & decryption.

Basic Example

The high level steps are:

  1. Create the encryptor method, 00000000000000000000000000000001 should be treated as a secret and byte[16] is trash per the comments below
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public byte[] Encrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = "00000000000000000000000000000001"u8.ToArray(); // Key length is important, 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes
// this example uses a 16-byte key (128-bit)
// the `u8` bit tells the compiler to treat the string as a UTF-8 encoded string
aes.IV = new byte[16]; // IV is used to ensure that even if the same plaintext is encrypted multiple times, the ciphertext will be different
// The IV should be randomly generated for each encryption operation. Reusing the same IV weakens the encryption significantly

using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using var ms = new MemoryStream();
using var stream = new CryptoStream(ms, encryptor, CryptoStreamMode.Write); // CryptoStream performs cryptographic transformations on the data written to it
stream.Write(data, 0, data.Length);
stream.FlushFinalBlock(); // This is crucial. AES, in certain modes of operation such as Cipher Block Chaining (CBC), uses padding,
// FlushFinalBlock() ensures that any padding required by the encryption algorithm is added to the end of the encrypted data and that the final block is completely processed.
return ms.ToArray();
}
  1. Create the decryptor method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public byte[] Decrypt(byte[] cipherText)
{
using var aes = Aes.Create();
aes.Key = "00000000000000000000000000000001"u8.ToArray();
aes.IV = new byte[16];

using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using var msDecrypt = new MemoryStream(cipherText);
using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
using var msPlain = new MemoryStream();
csDecrypt.CopyTo(msPlain); // Simpler way to read the entire stream, could also use StreamReader
// CopyTo is a convenient way to read the entire contents of a stream.
return msPlain.ToArray();
}
  1. Test the code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var originalText = "This is something Id like to Encrypt for network transport.";
Console.WriteLine($"OriginalText:\n {originalText}");
Console.WriteLine("--------------------------------------------------------------\n");

var dataToEncrypt = Encoding.UTF8.GetBytes(originalText);
var encryptedData = Encrypt(dataToEncrypt);
var encryptedBase64 = Convert.ToBase64String(encryptedData); // Good for storage/transmission
Console.WriteLine($"Encrypted (and converted Base64 for storage/transmission):\n {encryptedBase64}");
Console.WriteLine("--------------------------------------------------------------\n");

var decryptedFromBase64 = Convert.FromBase64String(encryptedBase64); // Back from storage/transmission
var decryptedData = Decrypt(decryptedFromBase64);
var decryptedText = Encoding.UTF8.GetString(decryptedData);
Console.WriteLine($"Decrypted:\n {decryptedText}");

This will output

1
2
3
4
5
6
7
8
9
10
OriginalText:
This is something Id like to Encrypt for network transport.
--------------------------------------------------------------

Encrypted (and converted Base64 for storage/transmission):
jALGsEjOBWur5Yd4lz6lnk8k/55u1d12Xf9D905FfcJbE3UCXZSHBLn/60tzSFq8vqM4cgUSEGn2YX2lxqATOg==
--------------------------------------------------------------

Decrypted:
This is something Id like to Encrypt for network transport.

Refactoring to a better design

  1. Pop the Encrypt and Decrypt methods into a class, change their key,iv to use the private members.
1
2
3
4
5
6
7
8
9
10
public class AesEncryptionService(string key, string iv)
{
private readonly byte[] _key = SHA256.HashData(Encoding.UTF8.GetBytes(key)); // takes the input key string, converts it to a byte array using UTF8 encoding
// and then calculates the SHA256 hash of that byte array
// the result of the SHA256 hash (32 bytes long) is what's actually used as the AES encryption key
private readonly byte[] _initializationVector = SHA256.HashData(Encoding.UTF8.GetBytes(iv)).Take(16).ToArray();
// same as the above but we only need 16 bytes
// the IV is not strictly a nonce in all AES modes of operation, though it serves a similar purpose in many modes

...
  1. You can then instanciate the class and pass the key and iv you want to use
1
2
3
4
var key = "00000000-0000-0000-0000-000000000001";
var iv = "00000000-0000-0000-0000-000000000002";

var service = new AesEncryptionService(key, iv);

Complete code is here https://github.com/carlpaton/EncryptDecryptDemo

But I want to Encrypt an object

Calm down, you just serialize it 🐒

The examples above encrypt and decrypt the text This is something Id like to Encrypt for network transport., you could have an object instance thats needed to be transported/persisted:

1
2
var obj = new MyObj { Id = 1, Foo = "bar" };
originalText = JsonConvert.SerializeObject(obj);

So if you plug that into the code the output is:

1
2
3
4
5
6
7
8
9
10
OriginalText:
{"Id":1,"Foo":"bar"}
--------------------------------------------------------------

Encrypted (and converted Base64 for storage/transmission):
kv+TWU0M14jtL9qvGhlOZLcAkCPP+kv9c0oZIKDHHYM=
--------------------------------------------------------------

Decrypted:
{"Id":1,"Foo":"bar"}

References