AES Encryption in .NET application

Security of data sent across API’s is of paramount importance to ensure the proper functioning and integrity of a project. APIs serve as bridges that allow different software systems to communicate and exchange information. Any sensitive or confidential data transmitted through APIs must be protected to prevent unauthorized access, tampering, or exposure.
Staying up to date with the latest developments and incorporating new features can lead to improved functionality, better user experiences, and enhanced security in software applications and systems.
Crucial values for today’s topic are Integrity, Confidentiality, and Authenticity. Remember them.

What is AES Encryption?

Advanced Encryption Standard (AES) is one of the most widely used algorithms by application developers to protect confidential data with a single secret key.
It is a symmetric key algorithm, meaning the same key is used for both encrypting and decrypting the data.
Supports 4 different key sizes:

  1. Triple DES (obsolete in .NET),
  2. AES-128-bit key,
  3. AES-192-bit key,
  4. AES-256-bit key (default in .NET 7).
Figure 1: Aes class default values in .NET
Source: .NET 7 code
Figure 2: SymmetricAlgorithm class
Source: .NET 7 code
Figure 3: Encryption element sizes in bytes
Source: Dawid “DeVinczi” Słysz/Author

Each key size has a different block size, which will be used during encryption.
Now look at Figure 1.
Basically, the default implementation has:
BlockSizeValue = 128 (bits) => 16 bytes
KeySizeValue = 256 (bits) => 32 bytes

.NET provides the highest available element sizes to proceed with encryption.
Now, we will be using one of the block cipher modes of operation techniques to encrypt data.

What exactly is a cipher mode of operation?

The cipher mode of operation determines the cryptographic algorithm that will perform the block transformation.
Here are some of the cipher modes of operation that you can use with AES:

  • Available on .NET using AES class
    • Electronic Codebook (ECB)
    • Cipher Block Chaining (CBC)
    • Cipher Text Stealing (CTS)
    • Cipher Feedback (CFB)
  • Unavailable in .NET AES class
    • Output Feedback (OFB)
    • Counter Mode (CTR)
    • Galois/Counter Mode (GCM)

Among these modes, Cipher Block Chaining (CBC) has garnered significant popularity. At present, CBC is the default choice for many programming libraries in .NET, as shown in Figure 1.
It offers good confidentiality but lacks authenticity and furthermore integrity.
It will cause some problems after the implementation, but I will try to make it clear why.

“Talk is cheap. Show me the code” ~ Linus Torvalds

Standard (naive) implementation of AES in .NET

Figure 4: Simplified graph of how encryption looks like in most cipher modes of operation
Source: Dawid “DeVinczi” Słysz/Author

For basic AES implementation in .NET, we take the AES.Create() method from the System.Security.Cryptography namespace. This method creates an instance of the AES algorithm for encryption and decryption purposes.

The interface is very straightforward and self-explanatory. In the interface above, our primary emphasis is on encrypting strings. We make the assumption that all initial strings are encoded in UTF-8 format.
As you can see, my methods require two additional arguments:

  1. Secret Key: A 256-bit (32-byte) key that’s used to both encrypt and decrypt data. This key should be kept secret and shared only with authorized parties.
  2. Initialization Vector (IV): A 128-bit (16-byte) value is used to introduce an element of randomness into the encryption process (explanted in Figure 4).
    Kept same as the secret key in (e.g., appsettings.json)

Below, you will find a description of the implementation of the concrete class version:

By default, the AES algorithm uses the following settings (go to Figure 1 & Figure 2):

  1. Padding: PKCS7 (Public Key Cryptography Standards 7) – This padding scheme is used to ensure that the input data is properly aligned with the block size of the encryption algorithm. Let’s make a quick example of how it works:
    We want to encrypt the string “Security”. Since it has only 8 characters, we need to add padding to use AES, which works in fixed-size blocks.
    The scheme is PKCS#7, defined in RFC 5652. This involves adding bytes with values equal to the number of missing bytes to fill the block.
    For instance, considering the string “Security”, we lack 8 bytes to complete a block. So, with padding, it becomes: “Security\x08\x08\x08\x08\x08\x08\x08\x08”. If the string’s length is a multiple of the block size, an extra block with padding is appended.
    For example, “SecurityInAspire” (16 bytes) with padding becomes: “SecurityInAspire\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10”.
    This is because the decryption algorithm expects that there will be some padding at the end of the encrypted message. The absence of an additional block with padding would lead to an error during decryption.
  2. Cipher Mode: AES-CBC (Cipher Block Chaining) – This mode involves chaining the ciphertext blocks, where each block is XORed with the previous ciphertext block before encryption.

Figure 5: Cipher Block Chaining mode of AES.
Source: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC)
But the first block also requires something to be XORed with. And that’s the initial vector (IV).

Now think for a minute.
Do you understand why the IV is so important?
Can you spot what’s wrong with the code snippet above?

If not, no worries. Look!
We reuse IV in all our encryption. This weakens and may destroy the AES; thus, it should be avoided. Look at the beginning of the graph above. Realize that with the same IV and encryption key, if two plaintexts have the same prefix blocks, their ciphertexts also share the same prefix blocks. We need to prevent this and have improved our implementation to use a random IV.

On a final note, relying on data provided by Microsoft and Cloudflare, CBC has been vulnerable for a while.

A common method for breaking cryptographic systems, particularly those employing block ciphers in modes like CBC, is the Padding Oracle attack.

In a few words, the AES class inherits from the SymmetricAlghoritm class by default padding PKCS#7 (Figure 2), and if we can have an impact on data to be decrypted in the application and the application clearly signals a padding error, we will be able to easily carry out the Padding Oracle attack.

Also, decrypting data without first performing a data integrity check and then authenticity using a Message Authentication Code (MAC), an asymmetric digital signature or authentication TAG, is a situation where decryption occurs without ensuring the data is valid.

Worth to read:

https://learn.microsoft.com/en-us/dotnet/standard/security/vulnerabilities-cbc-mode

https://blog.cloudflare.com/padding-oracles-and-the-decline-of-cbc-mode-ciphersuites/

Conclusion

It is really easy to shoot yourself in the foot while trying to use a .NET AES class in a real-world implementation.
You cannot defend against some malicious tampering with the ciphertext or padding Oracle attack because this method doesn’t provide basic cryptographic values like integrity or authenticity.
In programming always, it is better to be safe than sorry.
I will devote my next article to how to prepare secure, relevant, and up-to-date AES implementation in .NET.

Stay Tuned!