Generating a symmetric encryption key

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.

Note

There are no code differences whether you are using Bouncy Castle or Spongy Castle, as noted in the previous recipe.

How to do it...

Let's create a secure encryption key.

  1. Write the following function to generate a symmetric AES 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();
      }
  2. Create a random 32-byte initialization vector (IV) that matches the AES key size of 256 bit:
    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;
      }
  3. Write the following function to encrypt an arbitrary string:
    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;
      }
  4. For completeness, the preceding snippet shows how to decrypt. The only difference is that we call the 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.

How it works…

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.

There's more...

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.

Using AES-GCM for strong symmetric encryption

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");

Antipattern – setting the seed

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.

Android's PRNG bug

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.

Note

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.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.159.229