linkSecurity Best Practices: Symmetric Encryption with AES in Java and Android

In this article I will bring you up to speed on the Advanced Encryption Standard (AES), common block modes, why you need padding and initialization vectors and how to protect your data against modification. Finally I will show you how to easily implement this with Java avoiding most security issues.

linkWhat every Software Engineer should know about AES

AES, also known by its original name Rijndael, was selected by the NIST in 2000 to find a successor for the dated Data Encryption Standard(DES). AES is a block cipher, that means encryption happens on fixed-length groups of bits. In our case the algorithm defines 128 bit blocks. AES supports key lengths of 128, 192 and 256 bit.

Every block goes through many cycles of transformation rounds. I will omit the details of the algorithm here, but the interested reader is referred to the Wikipedia article about AES. The important part is that the key length does not affect the block size but the number of repetitions of transformation rounds (128 bit key is 10 cycles, 256 bit is 14)

Until May 2009, the only successful published attacks against the full AES were side-channel attacks on some specific implementations. (Source)

linkWant to encrypt more than one Block?

So AES will only encrypt 128 bit of data, but if we want to encrypt whole messages we need to choose a block mode with which multiple blocks can be encrypted to a single cipher text. The simplest block mode is Electronic Codebook or ECB. It uses the same unaltered key on every block like this:

Image from Wikpedia Image from Wikpedia

This is particularly bad since identical plaintext blocks are encrypted to identical ciphertext blocks.

Image encrypted with ECB block mode reveals patterns of the original ([try yourself](https://gist.github.com/patrickfav/13b2f727eaf91e3a72d87ac427485cb1)) Image encrypted with ECB block mode reveals patterns of the original (try yourself)

Remember to** never choose this mode unless you only encrypt data smaller than 128 bit.** Unfortunately it is still often misused because it does not require you to provide an initial vector (more about that later) and therefore_ seems_ to be easier to handle for a developer.

One case has to be handled with block modes though: what happens if the last block is not_ exactly_ 128 bit? That’s where **padding** comes into play, that is, filling the missing bits of the block up. The simplest of which just fills the missing bits with zeros. There is practically no security implication in the choice of padding in AES.

linkCipher Block Chaining (CBC)

So what alternatives to ECB are there? For one there is CBC which XORs the current plaintext block with the previous ciphertext block. This way, each ciphertext block depends on all plaintext blocks processed up to that point. Using the same image as before the result would be noise not distinguishable from random data:

Image encrypted with CBC block mode looks random Image encrypted with CBC block mode looks random

So what about the first block? The easiest way is to just use a block full of e.g. zeros, but then every encryption with the same key and plaintext would result in the same ciphertext. Also if you reuse the same key for different plaintexts it would make it easier to recover the key. A better way is to use a random **initialization vector (IV).** This is just a fancy word for random data that is about the size of one block (128 bit). Think about it like the salt of the encryption, that is, an IV can be public, should be random and only used one time. Mind though, that not knowing the IV will only hinder the decryption of the first block since the CBC XORs the ciphertext not the plaintext of the previous one.

When transmitting or persisting the data it is common to just prepend the IV to the actual cipher message. If you are interested on how to correctly use AES-CBC check out part 2 of this series.

linkCounter Mode (CTR)

Another option is to use CTR mode. This block mode is interesting because it turns a block cipher into a stream cipher which means no padding is required. In its basic form all blocks are numbered from 0 to n. Every block will now be encrypted with the key, the IV (also called_ nonce_ here) and the counter value.

Image from Wikpedia Image from Wikpedia

The advantage is, unlike CBC, encryption can be done in parallel and all blocks are depended on the IV not only the first one. A big caveat is, that an IV** must never be reused** with the same key because an attacker can trivially calculate the used key from that.

linkCan I be sure that nobody altered my message?

The hard truth:_ encryption does not automatically protect_ against data modification. It is actually a pretty common attack. Read here on a more thorough discussion about this issue.

So what can we do? We just add Message Authentication Code (MAC) to the encrypted message. A MAC is similar to a digital signature, with the difference that the verifying and authenticating key are practically the same. There are different variations of this method, the mode that is recommend by most researchers is called Encrypt-then-Mac. That is, after encryption a MAC is calculated on the cipher text and appended. You would usually use Hash-based message authentication code (HMAC) as type of MAC.

So now it starts getting complicated. For integrity/authenticity we have to choose a MAC algorithm, choose an encryption tag mode, calculate the mac and append it. This is also slow since the whole message must be processed twice. The opposite side has to to the same but for decrypting and verifying.

linkAuthenticated Encryption with GCM

Wouldn’t it be great if there were modes which handles all the authentication stuff for you? Fortunately there is a thing called authenticated encryption which simultaneously provides confidentiality, integrity, and authenticity assurances on the data. One of the most popular block modes that supports this is called **Galois/Counter Mode or GCM** for short (it is e.g. also available as a cipher suite in TLS v1.2)

GCM is basically CTR mode which also calculates an authentication tag sequentially during encryption. This authentication tag is then usually appended to the cipher text. Its size is an important security property, so it should be at least 128 bit long.

It is also possible to authenticate additional information not included in the plaintext. This data is called **associated data.** Why is this useful? For example the encrypted data has a meta property, the creation date, which is used to check if the content must be re-encrypted. An attacker could now trivially change the creation date, but if it is added as associated data, GCM will also verify this piece of information and recognize the change.

linkA heated discussion: What Key Size to use?

So intuition says: the bigger the better — it is obvious that it is harder to brute force a 256 bit random value than a 128 bit. With our current understanding brute forcing through all values of a 128 bit long word would require astronomically amount of energy, not realistic for anyone in sensible time (looking at you, NSA). So the decision is basically between infinite and infinite times 2¹²⁸.

AES actually has three distinct key sizes because it has been chosen as a US Federal Algorithm Apt at being used in various areas under the control of the US federal government [including the military]. (…) So the fine military brains came up with the idea that there should be** three** ”security levels”, so that the most important secrets were encrypted with the heavy methods that they deserved, but the data of lower tactical value could be encrypted with more practical, if weaker, algorithms. (…) So the NIST decided to formally follow the regulations (ask for three key sizes) but to also do the smart thing (the lowest level had to be unbreakable with foreseeable technology)(Source)

The argument follows: an AES encrypted message probably won’t be broken by brute forcing the key, but by other less expensive attacks (not currently known). These attacks will be as harmful to 128 bit key mode as to the 256 bit mode, so choosing a bigger key size doesn’t help in this case.

So basically 128 bit key is enough security for most of every use case with the exception of quantum computer protection. Also using 128 bit encrypts faster than 256 bit and the key-schedule for 128 bit keys seems to be better protected against related-key attacks (however this is irrelevant to most real-world uses).

linkAs a Side Note: Side Channel Attacks

Side channel attacks are attacks that aim to exploit issues specific to certain implementations. Encryption cipher schemes themself cannot be inherently protected against them. Simple AES implementations may be prone to timing and caching attacks among others.

As a very basic example: a simple algorithm that is prone to timing attacks is an equals() method that compares two secret byte arrays. If the equals() has a quick-return, meaning after the first pair of bytes that don’t match it ends the loop, an attacker can measure the time it takes for the equals() to complete and can guess byte for byte until all match.

Code that may be vulnerable to timing attacks by using a quick return Code that may be vulnerable to timing attacks by using a quick return

One fix in this instance would be to use a constant-time equals. Mind that it is often not trivial to write constant time code in interpreted languages like JVM languages.

Timing and caching attacks on AES are not merely theoretical and can even be exploited over a network. Although protecting against side channel attacks are mostly a concern of developers who implement cryptographic primitives, it is wise to get a sense of what coding practices may be detrimental to the security of the whole routine. The most general theme is, that the observable time-related behavior should not depend upon secret data. Additionally you should be carefully about what implementation to choose. For instance using Java 8+ with OpenJDK and the default JCA provider should internally use Intel’s AES-NI instruction set which is protected against most timing and caching attacks by being constant time and implemented in hardware (while still having good performance). Android uses it’s AndroidOpenSSLProvider which internally may use AES in hardware (ARM TrustZone) depending on the SoC, but Im not confident it has the same protection as Intels pedant. But even if you facilitate hardware, other attack vectors are available, for instance power analysis. Dedicated hardware exist that is specifically designed to protect against most of these issues, namely a hardware security module (HSM). Unfortunately these devices usually cost upwards of multiple thousand dollars(fun fact: your chip based credit card is also a HSM).

linkImplementing AES-GCM in Java and Android

So finally it gets practical. Modern Java has all the tools we need, but the crypto API might not be the most straight forward one. A mindful developer might also be unsure what length/sizes/defaults to use._ Note: if not stated otherwise everything applies equally to Java and Android._

In our example we use a randomly generated 128 bit key. Java will automatically choose the correct mode when you pass a key with 192 and 256 bit length. Note however, 256 bit encryption usually requires the JCE Unlimited Strength Jurisdiction Policy installed in your JRE (Android is fine).

1SecureRandom secureRandom = new SecureRandom();

2byte[] key = new byte[16];

3secureRandom.nextBytes(key);

4SecretKey secretKey = new SecretKeySpec(key, "AES");

Then we have to create our initialization vector. For GCM a 12 byte (not 16!) random (or counter) byte-array is recommend by NIST because it’s faster and more secure. Be mindful to always use a strong pseudorandom number generator (PRNG) like SecureRandom.

1byte[] iv = new byte[_12_]; //NEVER REUSE THIS IV WITH SAME KEY

2secureRandom.nextBytes(iv);

Then initialize your cipher. AES-GCM mode should be available to most modern JREs and Android newer than v2.3 (although only fully functional on SDK 21+). If it happens to be not available install a custom crypto provider like BouncyCastle, but the default provider is usually preferred. We choose an authentication tag of size 128 bit

1final Cipher cipher = Cipher._getInstance_("AES/GCM/NoPadding_"_);

2GCMParameterSpec parameterSpec = new GCMParameterSpec(_128_, iv); //128 bit auth tag length

3cipher.init(Cipher._ENCRYPT_MODE_, secretKey, parameterSpec);

Add optional associated data if you want (for instance meta data)

1if (associatedData != null) {

2 cipher.updateAAD(associatedData);

3}

Encrypt; if you are encrypting big chunks of data look into CipherInputStream so the whole thing doesn't need to be loaded to the heap.

1byte[] cipherText = cipher.doFinal(plainText);

Now concat all of it to a single message

1ByteBuffer byteBuffer = ByteBuffer._allocate_(iv.length + cipherText.length);

2byteBuffer.put(iv);

3byteBuffer.put(cipherText);

4byte[] cipherMessage = byteBuffer.array();

Optionally encode it with e.g. Base64 if you require a string representation. Android does have a standard implementation of this encoding, the JDK only from version 8 on (I would avoid Apache Commons Codec if possible since it is slow and a messy implementation).

And that’s basically it for encryption. For constructing the message, the IV, the encrypted data and the authentication tag are appended to a single byte array. (in Java the authentication tag is automatically appended to the message, there is no way to handle it yourself with the standard crypto API).

It is best practice to try to wipe sensible data like a cryptographic key or IV from memory as fast as possible. Since Java is a language with automatic memory management, we don’t have any guarantees that the following works as intended, but it should in most cases:

1Arrays.fill(key,(byte) 0); //overwrite the content of key with zeros

Be mindful to not overwrite data that is still used somewhere else.

Now to the** decrypt** part; It works similar to the encryption. Initialize the cipher with the IV and add the optional associated data and decrypt:

1final Cipher cipher = Cipher._getInstance_("AES/GCM/NoPadding")**;**

2

3//use first 12 bytes for iv

4AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128**,** cipherMessage**,** 0**,**_ 12_)**;**

5

6cipher.init(Cipher._DECRYPT_MODE**_,** secretKey**,** gcmIv)**;**

7

8if (associatedData != null) {

9 cipher.updateAAD(associatedData);

10}

11

12//use everything from 12 bytes on as ciphertext

13byte[] plainText = cipher.doFinal(cipherMessage**,**_ 12**_,** cipherMessage.length -_ 12_)**;**

That’s it! If you like to see a full example check out my Github project Armadillo where I use AES-GCM or this minimal working sample on Gist.

linkSummary

There are 3 properties we want for securing our data

AES with Galois/Counter Mode (GCM) block mode provides all those properties and is fairly easy to use and is available in most Java/Android environments. Just consider the following:

linkFurther Reading

Security Best Practices: Symmetric Encryption with AES in Java and Android: Part 2_ If you can’t use authenticated encryption like AES+GCM, this article will show how and why to use AES+CBC with…_proandroiddev.com

linkReferences

patrickfav/armadillo_ A shared preference implementation for secret data providing confidentiality, integrity and authenticity . Per default…_github.com

patrickfav/bytes-java_ Bytes is a utility library that makes it easy to create, parse, transform, validate and convert byte arrays in Java…_github.com

This article was published on 4/19/2020 on medium.com.

Security Best Practices: Symmetric Encryption with AES in Java and AndroidWhat every Software Engineer should know about AESWant to encrypt more than one Block?Cipher Block Chaining (CBC)Counter Mode (CTR)Can I be sure that nobody altered my message?Authenticated Encryption with GCMA heated discussion: What Key Size to use?As a Side Note: Side Channel AttacksImplementing AES-GCM in Java and AndroidSummaryFurther ReadingReferences

Home

OpenSourcechevron_right



Patrick Favre-Bulle 2020