USING THE ADVANCED ENCRYPTION STANDARD IN ANDROID
Created: April 18, 2012 | Updated: April 26, 2012

Introduction

Since the earliest releases, Android™ devices have featured state-of-the-art security software, including library operations to encrypt, decrypt, generate a message authentication code, create a randomized password and so on. In this technical article I will focus on one specific encryption algorithm, AES, and show you how to use it securely.

AES really is your "go-to algorithm" for encryption. It's used in many, many places in computer science, such as the 802.11i wireless protocol standard (sometimes known as WPA2). Earlier Wi-Fi versions used "Wired Equivalent Privacy" for password security. That name falsely implied that WEP was as secure as a wired connection. It was not, and both versions of WEP have been "deprecated as they fail to meet their security goals."

crypto_algorithms_on_razr.png

Figure 1: Representative cryptographic algorithms present on a Motorola RAZR

You can check exactly what algorithms are present on your device by plugging the sample code from this page into the "Hello World" app (i.e. integrate the code into Hello World and display the results in a TextView in a ScrollView). On my DROID RAZR™ running API level 10 (Gingerbread MR2), it reports over 150 algorithms, a representative sample of which is shown in Figure 1. Some of the names are aliases for the same algorithm. The numbers like 2.16.840.1.101.3.4.2 are machine-friendly, hierarchical aliases for algorithms.

Support for cryptographic algorithms is provided by a cut-down version of the open source Bouncy Castle security library. To lower memory footprint, Google removed support for a number of standard but less frequently used BC algorithms, such as RC5, RC6, Camellia, Serpent and Skipjack. You can see the exact list of removed algorithms by reading the source of file BouncyCastleProvider.java, to review the names that have been commented out. The Ice Cream Sandwich release updated the code to the then-current (but now supplanted) version of Bouncy Castle, version 1.46.

Developers who need a more recent version or the algorithms that Google dropped can install the Spongy Castle jar file. Spongy Castle is a re-packaging of the full Bouncy Castle library for Android, carried out by public-spirited developer Roberto Tyley. The Spongy Castle project is on github at https://github.com/rtyley/spongycastle.

From the screen snapshot (Figure 1), you can see that the cryptographic algorithms are organized into five groups:

  • Ciphers – these are classic encryption and decryption algorithms.
  • Key agreements – these implement a protocol for two parties to securely exchange a secret key between them, over an insecure channel.
  • Message authentication codes – these are algorithms used to check that a message (file, etc) has not been corrupted or changed.
  • Message digests – these are hash functions that offer a "fingerprint" of a document. These are frequently used as the basis of a message authentication code.
  • Signatures – these are algorithms that authenticate the origin of a file or message. Google requires that all apk files in the Google Play store are signed by a code that is unique to each developer. Thus each app in Google Play can be traced to the developer who put it there.

The Bouncy Castle APIs are not exposed directly. Developers access them through the java.crypto and java.security packages. Java security algorithms are pluggable modules, allowing libraries from different providers to be configured for use without changing the apps that use them.

Advanced Encryption Standard details

AES is the "Advanced Encryption Standard." It was adopted as a federal standard (U.S.FIPS PUB 197) in 2001. A couple of years later, with the benefit of practical experience, the U.S. Government announced that AES is authorized to protect classified information.

Let's review a little terminology. Encryption takes a plaintext message and runs it through an algorithm (called a cipher), which encrypts it to create a ciphertext. Most ciphers have a secret key as a parameter. The encryption algorithm is not easily reversed (decrypting the ciphertext) without the secret key. Knowing just the ciphertext should give as little information as possible (ideally none) about the plaintext. But if you have the ciphertext and the key, you will be able to recover the plaintext, as represented in Figure 2.

encryption_process.png

Figure 2: Plaintext, key, ciphertext

AES is a symmetric key algorithm, meaning that the same key is used for encryption and decryption. Single key or same key would be a more descriptive term. There are other algorithms, known as asymmetric key algorithms, in which two different keys are used – one for encrypting, and a second one for decrypting. This leads to some interesting novel properties, but is a topic for a different article.

AES can be used with 3 different lengths of key – keys are 128 bits, 192 bits, or 256 bits long (16, 24, or 32 bytes respectively). Three key lengths are defined because of the security/effort tradeoff – the larger the key, the more secure the cipher, but also the slower the algorithm becomes. Being able to choose different levels of security and computing effort is particularly important for low CPU power mobile devices. Longer keys are more resistant to attacks, but require more time and power to encrypt and decrypt. In 2003, the U.S. Government approved:

  • AES with 128 bit key length for SECRET classified information. The U.S. government definition is SECRET information would cause "serious damage" to national security if it were publicly available.
  • AES with 192 or 256 bit key length for TOP SECRET information. TOP SECRET is the highest (publicly known) level of classification of material on a U.S. national level. Such material would cause "exceptionally grave damage" to national security if made publicly available.

When you have a file or message that you want to keep top secret or merely secret, AES is the default algorithm to use. AES is a "block cipher," meaning it operates on fixed length blocks of plaintext. The AES block length is fixed at 128 bits (16 bytes). If your plaintext message is shorter than 16 bytes, it must be padded out to exactly 16 bytes. If your plaintext message is not an exact multiple of 16 bytes, the last block must be padded so that it is exactly 16 bytes long. You can specify that the algorithm should take care of the padding for you.

AES algorithm mode configuration

As well as the key length, there are a couple of other configuration choices to make when you use AES (these apply to some other ciphers too). These configuration choices are termed the mode of the cipher.

The first choice is the exact kind of padding you want. Originally, random characters were added to the end of the message. This policy was rethought after an incident where a random padding phrase was interpreted as part of the message, with disastrous results.

The current best practice is to always add padding (even if the message is an exact multiple of the block size) and to use the PKCS7 scheme. In PKCS7 padding, when you add N bytes of padding, each padding byte is set to the value N. So it's unambiguous. When you decrypt the final block, the algorithm always looks at the last byte, and removes that many bytes from the end of the message.

The second choice concerns the processing of each block. By default, AES encrypts each block using the key, and doesn't do any additional processing. This mode is called Electronic Code Book or "ECB." The problem with ECB is that identical blocks in the plaintext are encrypted into identical blocks in the ciphertext, and this reveals information to an attacker.

To prevent this "tell," there is a mode where a previously encrypted block is XOR'd with the current block before encryption. This mode is called Cipher Block Chaining, or "CBC." It hides patterns in the message, but has the disadvantage that a single bit of corruption in the ciphertext rendering all following blocks unreadable. CBC is shown in Figure 3. There are a number of alternative modes that can be used too. CTR (Counter) mode is a popular choice, but we'll stick with CBC in this example.

Figure 3: Cipher Block Chaining (CBC) mode

When CBC mode is used, you have to provide an additional block of data so the same XOR operation can be done on the first block of the message (which doesn't have a "previous" block to XOR with). This additional block of data is termed an Initialization Vector, or "IV." The IV should be 16 random bytes. You don't have to keep the IV value secret, but you have to remember it or else you will not be able to decrypt the ciphertext.

There is one more configuration choice. Many algorithms use a salt or seed, which is a sequence of random bytes, to use as a starting point for an algorithm. The length of the salt depends on the algorithm, and what you are using the salt for. As with an IV, you don't have to keep the salt secret, but you most definitely have to remember its exact value.

The algorithm, mode and padding are specified in a Java string, separated by a "/" character. So our choice of AES algorithm, CBC mode, and PKCS7 padding would be expressed in Java by a string like this:

final String CIPHERMODEPADDING = "AES/CBC/PKCS7Padding";

You must decrypt a ciphertext using exactly the same algorithm, mode, padding, key, initialization vector and salt/seed that were used to encrypt it. If you change even one bit of these configuration parameters, you will not be able to recover the plaintext.

Creating a secret key

AES can be used with a key that is 16, 24, or 32 bytes long. People aren't really good at remembering random bytes that long and tend to write them down (which adversely impacts security). To avoid this situation, and help with the randomness of keys, we generally remember a shorter passphrase, and use that to generate the actual secret key whenever we need it. In summary, creating a secret key starting from a passphrase is a four-step process:

  1. Create a key specification object to hold all the configuration parameters of the key we want to end up with. There are about 15 different subclasses of the KeySpec. The subclass that matches what we want to do is PBEKeySpec. PBE stands for "Password-Based Encryption". We're going to transform a human-friendly password into a binary key to use in encryption. So we'll create a PBEKeySpec object.
  2. Get a SecretKeyFactory object, telling the factory object which algorithm to use when we generate the secret key. The algorithm that we use here to generate the key is known as "PBEWITHSHAANDTWOFISH-CBC," and its details are not relevant. There are plenty of alternative algorithms that we could specify for use in key generation. I prefer PBKDF2 (Password-Based Key Derivation Function #2) for this purpose, but it is not included in the Android Bouncy Castle subset.
  3. Generate the secret key by passing the key specification from Step 1 into the factory from Step 2.
  4. Create a second secret key spec, using the raw bytes from the secret key and associate that spec with the AES algorithm.

Looking at the steps in more detail, with code, the steps are below.

1. Create a key specification object to hold all the configuration parameters of the key we want to end up with. These parameters are:

  • The short human-friendly passphrase that we start with
  • A salt, which has a random value to make it more difficult for an attacker to use rainbow tables (pre-prepared tables of keyspecs for likely passphrases)
  • The number of iterations of the hashing function. This is the number of times that the password will be hashed during the derivation of the symmetric key. The higher the number, the more secure the key.
  • The length of the secret key you want

There are about 15 different subclasses of java.security.spec.KeySpec. The subclass that processes the configuration parameters that we want to use is PBEKeySpec. We're going to transform a human-friendly password into a binary key to use in encryption. So we'll create a PBEKeySpec object. The code to specify the key looks like this:

char [] humanPassphrase = promptForMyPass();
byte [] salt = {0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF};  //must save this!
final int HASH_ITERATIONS = 10000;
final int KEY_LENGTH= 256;
PBEKeySpec mykeyspec = new PBEKeySpec(humanPassphrase, salt, HASH_ITERATIONS, KEY_LENGTH);

We must remember the passphrase (and keep it secret) and save the salt value, so we can generate the same secret key again next time. The salt value does not need to be kept secret.

2. Get a SecretKeyFactory object, telling the factory object which algorithm to use when we generate the secret key. Step 1 defined what we start with (the passphrase), what we use in the calculation (iteration count and salt), and a characteristic of the key we want to end up with (its length). Step 2 says which algorithm to use to generate the key. The algorithm that we use, PBEWITHSHAANDTWOFISH-CBC, is "Password-Based Encryption with the Secure Hash Algorithm and Twofish encryption using CBC". There are plenty of alternative algorithms that we could specify for use in key generation. The code looks like:

final String KEY_GENERATION_ALG = "PBEWITHSHAANDTWOFISH-CBC";
try {
  SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(KEY_GENERATION_ALG);
} catch (NoSuchAlgorithmException nsae) {
  Log.e("AESdemo", "no key factory support for PBEWITHSHAANDTWOFISH-CBC" );
}

Algorithms like PBEWITHSHAANDTWOFISH-CBC that transform a short character-based password into a longer binary key are known as key lengtheners or key stretchers. When the PBKDF standard was published 12 years ago, the recommended minimum iteration count was 1000. The iteration count is a parameter so that developers can easily increase it as CPUs increase in power. 10,000 is a reasonable number at the time of writing, so that's what we specified in Step 1.

3. Generate the secret key by using the generateSecret method in the factory, and passing it the PBE key specification that we created in Step 1. The code is:

try {
 SecretKey sk = keyfactory.generateSecret(mykeyspec);
} catch (InvalidKeySpecException ikse) {
 Log.e("AESdemo", "invalid key spec for PBKDF2" );
}

4. Create a second secret key spec, using the raw bytes from the secret key and associate that spec with the AES algorithm. The code is:

byte[] skAsByteArray = sk.getEncoded();
SecretKey skforAES = new SecretKeySpec(skAsByteArray, "AES");

At this point we have started from a password phrase, and used that to generate a secret key that can be used with AES. We could write the secret key to a file on a secure server, but the whole point of generating it is that we don't need to save a copy of the secret key, as long as we remember all the values that went into making it. When we need it again (e.g. to decrypt), we will generate it again.

Encrypting

After all that preparation to generate a secret key, encryption is straightforward. Again, it's a multi-step process:

  1. Get an instance of the cipher, specifying the mode and padding, as we saw above.
  2. Initialize the cipher, passing in a flag to say if you are encrypting or decrypting, the secret key spec from step 4 above, and a randomly chosen initialization vector. You can use a different initialization vector for each message, if you send the initialization vector in plaintext along with an encrypted message. You must remember the IV you used.
  3. Call update, passing in a byte array that holds 1 or more blocks of input plaintext.
  4. Call dofinal, passing in 1 or more blocks of input ending with the final block.

If you have the complete plaintext message, you can skip Step 3 and pass the entire plaintext in as Step 4. Step 3 allows you to start encrypting or decrypting an incomplete message that you are in the process of receiving. Putting the code together, it looks like this:

// step 1 - get an instance of the cipher, specifying the mode and padding
   final String CIPHERMODEPADDING = "AES/CBC/PKCS7Padding";
   byte [] iv = {0xA,1,0xB,5,4,0xF,7,9,0x17,3,1,6,8,0xC,0xD,91};  // must save this
   IvParameterSpec IV = new IvParameterSpec(iv);
   Cipher c = Cipher.getInstance(CIPHERMODEPADDING);

   // step 2 - initialize the cipher
   c.init(Cipher.ENCRYPT_MODE, sk, IV);

   // step 3 - not needed, as we have all the blocks on hand

   // step 4 - call doFinal()
   c.doFinal(plaintext);

Decrypting

Decrypting is the reverse of encryption. The steps are:

  1. Get an instance of the cipher.
  2. Initialize it for decrypting with the same secret key and the same IV.
  3. doFinal(), since we have all the blocks.

The code looks like:

String decrypted = new String(decrypt(CIPHERMODEPADDING, skforAES, IV, ciphertext));

byte [] decrypt(String cmp, SecretKey sk, IvParameterSpec IV, byte[] ciphertext) {
	   try {
		// Step 1 - get instance of the cipher
		   Cipher c = Cipher.getInstance(cmp);

		// Step 2 - initialize with mode, secret key, and IV
		   c.init(Cipher.DECRYPT_MODE, sk, IV);

		// step 3 - we have all the blocks, so decrypt with doFinal
		 byte[] decrypted = c.doFinal(ciphertext);

	  } catch (NoSuchAlgorithmException nsae) {
		   Log.e("AESdemo", "no cipher getinstance support for "+cmp );
	  } catch (NoSuchPaddingException nspe) {
		   Log.e("AESdemo", "no cipher getinstance support for pad " + cmp );
	   } catch (InvalidKeyException e) {
		   Log.e("AESdemo", "invalid key exception" );
	  } catch (InvalidAlgorithmParameterException e) {
		   Log.e("AESdemo", "Invalid algorithm parameter exception" );
	   } catch (IllegalBlockSizeException e) {
		   Log.e("AESdemo", "Illegal block size exception" );
	  } catch (BadPaddingException e) {
		   Log.e("AESdemo", "Bad padding exception" );
	  }
  return null;
}

Conclusion

Figure 4 shows a screen snapshot of the results of a complete encryption and decryption on Android, with display of the final message. I chose this plaintext from Lewis Carroll because of his evident fondness for clear explanations.

cryptotest_results.png

Figure 4: Screen snapshot of a complete encryption and decryption

You can find occasional reports of AES being cracked, such as this one. They are usually exaggerations. All current known attacks on AES are computationally infeasible. However, there are side channel attacks that target non-algorithmic parts of an implementation that "leak" data (things like CPU times to execute part of the algorithm, or cache contents). Bear in mind AES protects against adversaries who eavesdrop. It does not protect against adversaries who can tamper with traffic and inject packets. Such adversaries will not be able to forge messages, but traffic tampering will render messages unreadable by the intended recipient. To provide guarantees of traffic integrity as well as confidentiality, you need to use a technique called "authenticated encryption", which is usually based on block ciphers like AES.

Finally, be careful when you use encryption. It is easy to make small mistakes which degrade the security measures. For real world applications, you should have a security professional review your design and approach.

Peter van der Linden
Android Technology Evangelist

 

Created: April 18, 2012
Updated: April 26, 2012

ECCN 5D992.a: In accordance with United States Export Administration Regulations (EAR), and specifically the Commerce Control List (CCL), this item has been classified 5D992.a. Export or re-export of this commodity and compliance with the U.S. Export Administration Regulations is ultimately the responsibility of the exporter. For more detailed information related to export or re-export of this item, please consult the EAR at http://www.access.gpo.gov/bis/ear/ear_data.html.

Copyright © 2012, Motorola Mobility, Inc. All rights reserved unless otherwise explicitly indicated. Sample source code written by Motorola Mobility, Inc. is provided to you under the conditions of the Motorola Modified BSD License.

print the page