5

I had tried to run the following AES/ CBC/ PKCS5Padding encryption and decryption code, with SHA-1 as key generation, in Nexus 5. It works very well so far.

However, my only concern is, Is AES/ CBC/ PKCS5Padding encryption decryption algorithm and SHA-1 hashing algorithm available in all type of Android devices?

Is there any chance that the following code will fail to run on certain Android devices? If so, is there any fall back plan?

AES/ CBC/ PKCS5Padding

// http://stackoverflow.com/questions/3451670/java-aes-and-using-my-own-key
public static byte[] generateKey(String key) throws GeneralSecurityException, UnsupportedEncodingException {
    byte[] binary = key.getBytes("UTF-8");
    MessageDigest sha = MessageDigest.getInstance("SHA-1");
    binary = sha.digest(binary);
    // Use only first 128 bit.
    binary = Arrays.copyOf(binary, 16);
    return binary;
}

// http://stackoverflow.com/questions/17322002/what-causes-the-error-java-security-invalidkeyexception-parameters-missing
public static String encrypt(byte[] key, String value) throws GeneralSecurityException {
    // Argument validation.
    if (key.length != 16) {
        throw new IllegalArgumentException("Invalid key size.");
    }

    // Setup AES tool.
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));

    // Do the job with AES tool.
    byte[] original = value.getBytes(Charset.forName("UTF-8"));
    byte[] binary = cipher.doFinal(original);
    return Base64.encodeToString(binary, Base64.DEFAULT);
}

// // http://stackoverflow.com/questions/17322002/what-causes-the-error-java-security-invalidkeyexception-parameters-missing
public static String decrypt(byte[] key, String encrypted) throws GeneralSecurityException {
    // Argument validation.
    if (key.length != 16) {
        throw new IllegalArgumentException("Invalid key size.");
    }

    // Setup AES tool.
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));

    // Do the job with AES tool.
    byte[] binary = Base64.decode(encrypted, Base64.DEFAULT);
    byte[] original = cipher.doFinal(binary);
    return new String(original, Charset.forName("UTF-8"));
}

Usage

byte[] key = generateKey("my secret key");
String ciphertext = encrypt(key, "my plain content");
String plainContent = decrypt(key, ciphertext);
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Try it out in all available emulator images, but I see no reason this shouldn't be implemented everywhere. If you still don't want to try it yourself, then use the BouncyCastle/SpongyCastle provider by default. – Artjom B. Apr 11 '15 at 09:42
  • possible duplicate of [What crypto algroithms does Android Support](http://stackoverflow.com/questions/7560974/what-crypto-algroithms-does-android-support) – Oleg Estekhin Apr 11 '15 at 10:10
  • @OlegEstekhin It think that's a bit of overdoing it with regards to this question. This means that the asker still has to test on all emulators, something you may only want to do after designing an application. – Maarten Bodewes Apr 11 '15 at 15:06

2 Answers2

5

No, it's unlikely to the extreme that it will fail. The Android API has been derived from the Java API's. The Java API's have contained the "AES/CBC/PKCS5Padding" since version 1.4.

As for "SHA-1", that's an even older algorithm, which has been supported since time began.

Beware not to use "PKCS7Padding" instead. Java uses "PKCS5Padding" as replacement, "PKCS7Padding" support may be sketchy even if it means the same thing.


Note that you should be using password based encryption (PBE) instead of AES/CBC and SHA-1. Especially using SHA-1 as key derivation method is particularly dangerous as you don't use a salt or work factor as a good Password Based Key Derivation Function such as PBKDF2 should. Basically only do this if you know your password contains enough entropy.

Using an all zero IV for the same key is worse though (as already indicated in the comments). It lets attackers find repeats of (the starting blocks of) plaintext input. Authenticated encryption (e.g. using HMAC-SHA-1) is always recommended and more or less required for transport mode encryption (as opposed to in-place encryption where plaintext/padding oracle attacks are not possible).

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I just realize I can't use "AES/CBC/PKCS5Padding", as it will break on some of my legacy encrypted data. The legacy encrypted data is using "AES". Is "AES" as common as "AES/CBC/PKCS5Padding" – Cheok Yan Cheng Apr 11 '15 at 15:08
  • Yes, it always evals to `"AES/ECB/PKCS5Padding"` on Java and Android as far as I know, even though it *officially relies on provider defaults*. So it may change for (future) providers. I would strongly recommend you to specify `"AES/ECB/PKCS5Padding"` instead, and realize it's *even less secure* than CBC with zero IV. ECB does not use the `IvParameterSpec`. – Maarten Bodewes Apr 11 '15 at 15:11
  • But my observation is that, using "AES" and "AES/ECB/PKCS5Padding" will yield different behavior. If I use "AES", using `cipher.init(Cipher.ENCRYPT_MODE, skeySpec);` without `IvParameterSpec` will work. However, if I use "AES/ECB/PKCS5Padding", without `IvParameterSpec`, it will fail. – Cheok Yan Cheng Apr 11 '15 at 15:14
  • That's exceedingly weird, can you shed more light on how and on which runtime the ciphertext was created? Could you print out `cipher.getProvider()`? – Maarten Bodewes Apr 11 '15 at 15:17
  • This is a workable AES example : https://gist.github.com/yccheok/e43aa336894dbf5106df I tested the code in Java 8 desktop. – Cheok Yan Cheng Apr 11 '15 at 15:26
  • This AES/CBC/PKCS5Padding example causes exception : https://gist.github.com/yccheok/3cdf27cf042ce23b7a56 during decrypt (Exception in thread "main" java.security.InvalidKeyException: Parameters missing) I tested the code in Java 8 desktop. – Cheok Yan Cheng Apr 11 '15 at 15:26
  • Yeah, that's what you get when you are trying to decrypt without specifying an IV. You need to at least specify an IV. Usually that's a zero IV or it is *prefixed to the ciphertext*. – Maarten Bodewes Apr 11 '15 at 15:29
  • But, isn't "AES" always evals to "AES/ECB/PKCS5Padding"? If so, why "AES" code example will succeed during decrypt, even I don't specific any IV? – Cheok Yan Cheng Apr 11 '15 at 15:31
  • ECB doesn't use an IV; all other modes, including CBC do use an IV. – Maarten Bodewes Apr 11 '15 at 15:32
  • Thanks! My bad eye sight miss up with "ECB" and "CBC". Now my doubt it cleared. Thanks! – Cheok Yan Cheng Apr 11 '15 at 15:34
  • Could you ask a separate question if you are still stuck choosing between `"AES"`, `"AES/ECB"` and `"AES/CBC"`? Note that we may need more information about the encryption procedure to be able to help you. – Maarten Bodewes Apr 11 '15 at 15:36
  • Hi, your info is rather helpful to me. I think need not to have another question. I had decided to stick with "AES/ECB", which I know (1) Is very common since Android 1.4 (2) "AES/ECB" will continue work with my legacy data, which are previously using "AES". As "AES" will always evaluated to "AES/ECB". (3) I need not to use IV for "AES/ECB". – Cheok Yan Cheng Apr 11 '15 at 15:46
3

This isn't answering your question directly, but...

cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));

Do not use this construct! It will break any security you think you're getting!

This invocation initialises your cipher object with an all-zeros initialisation vector. This is a very very very bad thing, especially with CBC: CBC is quite malleable, and doesn't do any integrity-protection. Make sure you generate your IV using SecureRandom or similar, and preferably use GCM or CCM.

  • But, isn't that if I encrypt using a random IV, and decrypt using **another** random IV, I will not able to recover my original source message? – Cheok Yan Cheng Apr 11 '15 at 11:45
  • You generate a random IV when you encrypt, and send it alongside the ciphertext. When you decrypt, you have to give the same IV to the decryption code. – Justin King-Lacroix Apr 11 '15 at 11:47
  • I realize if I use "AES" instead of "AES/CBC/PKCS5Padding", I can use `cipher.init(Cipher.DECRYPT_MODE, skeySpec);`. Do you know any potential loop hole, if I eliminate IvParameterSpec all at once? – Cheok Yan Cheng Apr 11 '15 at 15:10
  • 2
    Unfortunately I think this is indeed more a comment than an answer. – Maarten Bodewes Apr 11 '15 at 15:12
  • I think that means you're using ECB mode, which is also a bad idea. Read the Wikipedia article on block cipher modes, it will explain why in detail, but briefly, it means an attacker can potentially break the crypto by what amounts to a frequency analysis attack. But it depends on what "AES" actually means; the environment might actually do this for you in that case. – Justin King-Lacroix Apr 11 '15 at 15:13
  • Well that came up in the low quality posts queue, it should have been condensed in a comment. I hate to recommend deletion good infos like this but those are the rules. – Tom Tom Apr 12 '15 at 01:14