Basic Android Encryption Do’s and Don’ts

Vincent Huang
Jan 29, 2018 · 3 min read

This article is meant to be a collection of things I found useful to know while building an encryption layer within an Android app. If you have anything to contribute or to correct please let me know and I’ll make updates accordingly

Before proceeding I highly recommend reading Yakiv Mospan’s article on Android security. I found it to be an excellent foundation for getting started on encryption in Android. *Note that as of writing this there will be some overlap with Yakiv Mospan’s blog post as he is still updating it.

  1. Things to avoid
  2. Things to do
  3. Nice to know

1. Things to avoid

Do

Cipher.getInstance("AES/GCM/NOPADDING");

Don’t (will apply ECB by default)

Cipher.getInstance("AES");

Don’t use hard coded values for cipher initialization. Integrity of the cipher is compromised if it isn’t given unique information for every operation. Provide a SecureRandom to the init method of ciphers as the default values may not be random for older versions.

Do

SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[IV_LENGTH];
secureRandom.nextBytes(iv);
myCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

Don’t

myCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = myCipher.getIV();

2. Things to do:

Zero-out sensitive byte arrays. Supposedly, replacing each index in a byte array with 0 helps against in memory attacks. I’m hesitant on the accuracy of this due to all of the moving parts in the Android ecosystem, so take this with a grain of salt.

byte[] decrypt(byte[] dataToDecrypt, byte[] secretKey) {
//Decrypt data using cipher and secretKey
...
//Clear out secret key encoding byte array
Arrays.fill(secretKey, (byte) 0);
return decryptedData;
}

3. Nice to know:

Destroy method in SecretKey is unreliable If targeting ≤ API 25 app will crash with “method not found” exception. If api version is above 25, then the method will simply throw DestroyFailedException by default. I don’t have any recommendations outside of zeroing-out secret key byte arrays as the solutions I’ve seen are hacky and use reflection. If you have any suggestions please let me know.

Cipher accepts empty byte arrays but not null. I wasn’t aware that you could encrypt something that was empty. I guess this is more of a fun fact.

Use Base64 (Or others formats like Hex, Base32, etc) when handling byte arrays produced via encryption/decryption. Base64 facilitates transportation of bit data by turning it into intelligible characters that exist in most systems. A good strategy for encrypting things like Strings is to do String.byte(“UTF-8”) -> encrypt -> encode to Base64 String. Decoding on the other hand would be done in the opposite direction.

Encoding:

String encryptAndEncode(String data) {
Charset charSet = Charset.forName(UTF_8);
//encrypt method not implemented
byte[] encrypted = encrypt(data.getBytes(charSet));
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
}

Decoding:

String decodeAndDecrypt(String encrypted) {
Charset charSet = Charset.forName(UTF_8);
//decrypt method not implemented
byte[] data = decrypt(Base64.decode(encrypted, Base64.NO_WRAP));
return new String(data, charSet);
}

Interested in joining our team? WeWork is hiring software engineers in Tel-Aviv and New York. View openings