5

I am writing a simple app to encrypt my message using AES / CBC (mode). As my understanding CBC mode requires IV parameter but I don't know why my code work without IV parameter used. Anyone can explain why? Thanks.

The encrypted message printed: T9KdWxVZ5xStaisXn6llfg== without exception.

public class TestAES {

    public static void main(String[] args) {

        try {
            byte[] salt = new byte[8];
            new SecureRandom().nextBytes(salt);

            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            KeySpec keySpec = new PBEKeySpec("myPassword".toCharArray(), salt, 100, 128);

            SecretKey tmp = keyFactory.generateSecret(keySpec);
            SecretKeySpec key = new SecretKeySpec(tmp.getEncoded(), "AES");

            Cipher enCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            enCipher.init(Cipher.ENCRYPT_MODE, key);

            // enCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

            byte[] cipherBytes = enCipher.doFinal("myMessage".getBytes());
            String cipherMsg = BaseEncoding.base64().encode(cipherBytes);

            System.out.println("Encrypted message: " + cipherMsg);

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}
EricSchaefer
  • 22,338
  • 20
  • 63
  • 99
  • 1
    It works because Java picks a random IV. you can use `enCipher.getIV()` after init to see the values (and sent them to the other side). – eckes Jun 03 '16 at 17:53
  • https://doridori.github.io/Android-Security-Beware-of-the-default-IV/ As you were using `AES/CBC/PKCS5Padding` it was probably using the old `AndroidOpenSSL` implementation, which (sadly) defaults to a `[0]` IV – Dori Sep 28 '17 at 13:47

2 Answers2

6

When it is used without an IV, for certain types of ciphers including AES, it implicitly uses 0 IV. See Cipher class documentation.

The disadvantage of a null IV (or a deterministic IV) is that it is vulnerable to dictionary attacks. The requirement for IV is to prevent the same plain text block producing the same cipher text every time.

Community
  • 1
  • 1
Buddhima Gamlath
  • 2,218
  • 12
  • 25
  • 2
    Actually Java SE uses a random IV if you specify none. Your link points to Java card API. If you do not specify one you get a randome one, you do need to retrieve it with `cipher.getIV()` if you did not set it. – eckes Jun 03 '16 at 17:35
  • Depends on the `Provider` in use also https://doridori.github.io/Android-Security-Beware-of-the-default-IV – Dori Sep 28 '17 at 13:48
1

Like other users have said, it depends on the JCE provider. Java SE generates a random IV for you if you specify none.

Only Android1 and Javacard API use a blank IV, which is non-conforming to the Java Crypto spec, which states:

If this cipher requires any algorithm parameters that cannot be derived from the given key, the underlying cipher implementation is supposed to generate the required parameters itself (using provider-specific default or random values) if it is being initialized for encryption or key wrapping, and raise an InvalidKeyException if it is being initialized for decryption or key unwrapping. The generated parameters can be retrieved using getParameters or getIV (if the parameter is an IV).

If you do not specify the IV, in Java SE you get a random one, and will need to retrieve it with cipher.getIV() and store it, as it will be needed for decryption.

But better yet, generate a random IV yourself and provide it via IvParameterSpec.

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    SecureRandom rnd = new SecureRandom();
    byte[] iv = new byte[cipher.getBlockSize()];
    rnd.nextBytes(iv);
    IvParameterSpec ivParams = new IvParameterSpec(iv);

    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParams);

    byte[] ciphertext = cipher.doFinal(input.getBytes());

1 That could be because Android is Java-esque, like the Eminem-esque ad. Just guessing, that's all.

rustyx
  • 62,971
  • 18
  • 151
  • 210