1

I've been doing some learning/experimentation with the basic Java AES stuff, and I can't figure out where I'm doing things wrong. According to the docs, doFinal() should encrypt/decrypt everything in one shot, and it does appear to do so; however, on decryption, with an invalid IV, I can still decrypt most of the data. In the interest of security, please tell me where the heck I messed up in my test code?

Notes: I am not using BC or the Java Unlimited Strength Crypto Extensions. This code follows examples and guidance from The Oracle JCA Reference Guide. In the code below, I select AES with CFB (same results with CBC), generate a key, generate an IV, feed encrypt, nuke the IV, and feed decrypt, and ideally (if I understand CBC and CFB correctly) the nuked IV should render the decrypted output into garbage. But it does not...at least, not on my installation...

It should also be noted that GCM does work correctly (you'll no doubt see the commented-out algo string below), but it bothers me that CBC and CFB are acting this way. Reality-check, please?

    public static void encdec() throws Exception {
        // let's encrypt something!
        String message = "This is a super-secret message.  Don't read this message.  This message will self-destruct...";

        final String symmetricCipherSpec = "AES/CFB/PKCS5Padding";// "AES/GCM/NoPadding";
        final String symmetricKeySpec = "AES";

        System.out.println("\n\nSelecting plaintext cypher...");
        Cipher cipher = Cipher.getInstance(symmetricCipherSpec);

        // What did we get?
        System.out.println("--> Cipher Algorithm Selected: " + cipher.getAlgorithm());
        AlgorithmParameters parameters = cipher.getParameters();
        System.out.println("--> Parameters for algo:       " + parameters.getAlgorithm());
        System.out.println("--> block size:                " + cipher.getBlockSize());
        final int blockSize = cipher.getBlockSize();

        KeyGenerator keyGenerator = KeyGenerator.getInstance(symmetricKeySpec);
        keyGenerator.init(128);
        Key key = keyGenerator.generateKey();
        System.out.println("\nsymmetric key = " + Hex.encodeHexString(key.getEncoded()));

        // set up for encryption
        int tLen = 16;
        byte[] basicIV = new byte[tLen];
        for (int i = 0; i < basicIV.length; i++) {
            basicIV[i] = (byte) i;
        }

        byte[] messageToEncrypt = message.getBytes();
        IvParameterSpec ivSpec = new IvParameterSpec(basicIV);
        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
        byte[] encryptedMessage = cipher.doFinal(messageToEncrypt);

        System.out.println("\nencrypted     = " + Hex.encodeHexString(encryptedMessage));
        byte[] iv = cipher.getIV();
        System.out.println("iv            = " + Hex.encodeHexString(iv) + " (len=" + iv.length + ")");

        // reset IV to garbage.
        basicIV = new byte[tLen];
        ivSpec = new IvParameterSpec(basicIV);
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
        byte[] decrypted = cipher.doFinal(encryptedMessage);

        System.out.println("\ndecrypted     = " + new String(decrypted));
        System.out.println("original      = " + message);
        System.out.println("iv            = " + Hex.encodeHexString(cipher.getIV()) + " (len=" + cipher.getIV().length + ")");
    }
David Soroko
  • 8,521
  • 2
  • 39
  • 51
  • Mostly dupe https://stackoverflow.com/questions/16349441/first-8-byes-of-my-encrypted-data-corrupting-using-3des-and-cbc and I believe but didn't find more, and since this isn't really a programming issue crossdupe https://crypto.stackexchange.com/questions/2865/why-does-cbc-decryption-with-a-wrong-iv-still-give-readable-results https://crypto.stackexchange.com/questions/1129/can-cbc-ciphertext-be-decrypted-if-the-key-is-known-but-the-iv-not where it belongs. Also JCE 'unlimited' policy has been obsolete since 8u161 2 years ago, and before that didn't affect 128-bit. – dave_thompson_085 Nov 25 '19 at 21:01

1 Answers1

0

Nevermind... my understanding of CBC was flawed; this the code is working as intended. Per Wikipedia:

Decrypting with the incorrect IV causes the first block of plaintext to be corrupt but subsequent plaintext blocks will be correct. This is because each block is XORed with the ciphertext of the previous block, not the plaintext, so one does not need to decrypt the previous block before using it as the IV for the decryption of the current one.

  • BTW regarding your `// reset IV to garbage` comment - you may know this already but Java will initialize your byte array to be all 0, so not quite garbage/ – David Soroko Nov 25 '19 at 19:38
  • Yes, I did know that :) All the same, it was an IV different from the original, so one could say my comment was "hopeful" in what I expected the resultant output to be! – Matthew O'Connor Nov 25 '19 at 20:00