A symmetric key describes a key that is used for both encryption and decryption. To create cryptographically secure encryption keys in general, we use securely generated pseudorandom numbers. This recipe demonstrates how to correctly initialize the SecureRandom
class and how to use it to initialize an Advanced Encryption Standard (AES) encryption key. AES is the preferred encryption standard to DES, and typically used with key sizes 128 bit and 256 bit.
Let's create a secure encryption key.
public static SecretKey generateAESKey(int keysize) throws NoSuchAlgorithmException { final SecureRandom random = new SecureRandom(); final KeyGenerator generator = KeyGenerator.getInstance("AES"); generator.init(keysize, random); return generator.generateKey(); }
private static IvParameterSpec iv; public static IvParameterSpec getIV() { if (iv == null) { byte[] ivByteArray = new byte[32]; // populate the array with random bytes new SecureRandom().nextBytes(ivByteArray); iv = new IvParameterSpec(ivByteArray); } return iv; }
public static byte[] encrpyt(String plainText) throws GeneralSecurityException, IOException { final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, getKey(), getIV()); return cipher.doFinal(plainText.getBytes("UTF-8")); } public static SecretKey getKey() throws NoSuchAlgorithmException { if (key == null) { key = generateAESKey(256); } return key; }
Cipher.init()
method using the Cipher.DECRYPT_MODE
constant:public static String decrpyt(byte[] cipherText) throws GeneralSecurityException, IOException { final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, getKey(),getIV()); return cipher.doFinal(cipherText).toString(); }
For this sample, we have just stored the key and IV as a static variable; this isn't advisable for actual use. A simple approach would be to persist the key in SharedPerferences
with the Context.MODE_PRIVATE
flag so that a consistent key is available between application sessions. The next recipe develops this idea further to use an encrypted version of SharedPerferences
.
Creating a SecureRandom
object is simply a case of instantiating the default constructor. There are other constructors available; however, the default constructor uses the strongest provider available. We pass an instance of SecureRandom
to the KeyGenerator
class with the keysize
argument, and the KeyGenerator
class handles the creation of the symmetric encryption key. 256 bit is often touted as "military grade", and for most systems it is considered cryptographically secure.
Here we introduce an initialization vector which, in simple terms, increases the strength of the encryption, and is essential when encrypting more than one message/item. This is because messages encrypted with the same key can be analyzed together to aid message extraction. A weak IV is part of the reason why Wired Equivalent Privacy (WEP) was broken. So, it is recommended to generate a new IV for each message, and store it along with the cipher text; for example, you could pre-append or concatenate the IV to the cipher text.
For the actual encryption, we use an AES instance of the Cipher
object that we initiate in ENCRYPT_MODE
with the newly-generated SecretKey
. We then call cipher.doFinal
with the bytes of our plaintext input to return a byte array containing the encrypted bytes.
When requesting the AES encryption mode with the Cipher
object, a common oversight that is also present in Android documentation is to simply use AES
. However, this defaults to the simplest and less-secure ECB mode, specifically AES/ECB/PKCS7Padding
. Therefore, we should explicitly request the stronger CBC mode AES/CBC/PKCS5Padding
, as shown in the sample code.
Here we look at how to use a strong encryption mode called AES-GCM , and a common antipattern that reduces the security of the generated keys.
We noted that simply defining AES
does not default to the strongest mode. If we include the Spongy Castle libraries, we can use the much strong AES-GCM that includes authentication, and can detect if the cipher text has been tampered with. To use AES-GCM when defining the algorithm/transformation string, use AES/GCM/NoPadding
as shown in the following code:
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC");
Since Android Version 4.2, the default PseudoRandom Number Generator (PRNG) provider of SecureRandom
was changed to OpenSSL. This disables the ability, which existed previously, of Bouncy Castle provider to manually seed the SecureRandom
object. This was a welcome change as an antipattern emerged where developers were setting the seed.
byte[] myCustomSeed = new byte[] { (byte) 42 }; secureRandom.setSeed(myCustomSeed); int notRandom = secureRandom.nextInt();
In this code sample, we can see the seed being manually set to 42
, the result being that the notRandom
variable would always equal the same number. Although useful for unit tests, this defeats any enhanced security from using SecureRandom
to generate a cryptographic key.
As mentioned previously, the default provider of PseudoRandom Number Generator (PRNG) is OpenSSL since Android 4.2. However, in August 2013, a critical bug was discovered with the generation of random numbers. This was highlighted by the compromise of several Android Bitcoin wallet apps. The issue concerned the seeding of the secure random number generator; instead of using complex and individual system fingerprints, it was initialized to null. The result was similar to that of the antipattern secure keys that were generated earlier from a predictable number. The effected Android versions were Jelly Bean 4.1, 4.2, and 4.3.
A fix was noted in the Some SecureRandom Thoughts Android blog article and issued to Open Handset Alliance companies. However, it's recommended that you call this fix from your application's onCreate()
method in case the fix has not been applied to the device your app is running on.
For convenience, here's a gist provided by GitHub of the code from Google, which can be found at https://gist.github.com/scottyab/6498556.
SecureRandom
class in the Android Developer Reference guide at https://developer.android.com/reference/java/security/SecureRandom.htmlKeyGenerator
class in the Android Developer Reference guide at https://developer.android.com/reference/javax/crypto/KeyGenerator.html18.117.159.229