1

I want to decrypt an AES encrypted message in Java. I’ve been trying various Algorithm/Mode/Padding options from the standard library and from BouncyCastle. No luck :-(

The encrypting entity is written in Python and is already in production. Encrypted messages have already gone out, so I cannot easily change that part. The Python code looks like this:

from Crypto.Cipher import AES
import base64
import os
import sys

BLOCK_SIZE = 16
PADDING = '\f'

pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING

EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)

secret = 'XXXXXXXXXXXXXXXX'

cipher = AES.new(secret)

clear='test'
encoded = EncodeAES(cipher, clear)

print 'Encrypted string:>>{}<<'.format(encoded)

decoded = DecodeAES(cipher, encoded)

print 'Decrypted string:>>{}<<'.format(decoded)

Obviously AES is used, and I figured out that I have to use ECB mode. But I have not found a padding mode that works on the Java side. If the input fits within the block size and no padding is happening, I can decrypt the message in Java. If the message needs to be padded, decryption fails.

The Java code to decrypt looks like this:

public class AESPaddingTest {

    private enum Mode {
        CBC, ECB, CFB, OFB, PCBC
    };

    private enum Padding {
        NoPadding, PKCS5Padding, PKCS7Padding, ISO10126d2Padding, X932Padding, ISO7816d4Padding, ZeroBytePadding
    }

    private static final String ALGORITHM = "AES";
    private static final byte[] keyValue = new byte[] { 'X', 'X', 'X', 'X',
            'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' };

    @BeforeClass
    public static void configBouncy() {
        Security.addProvider(new BouncyCastleProvider());
    }

    @Test
    public void testECBPKCS5Padding() throws Exception {
        decrypt("bEpi03epVkSBTFaXlNiHhw==", Mode.ECB,
                Padding.PKCS5Padding);
    }

    private String decrypt(String valueToDec, Mode modeOption,
            Padding paddingOption) throws GeneralSecurityException {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);

        Cipher c = Cipher.getInstance(ALGORITHM + "/" + modeOption.name() + "/" + paddingOption.name());

        c.init(Cipher.DECRYPT_MODE, key);

        byte[] decValue = c.doFinal(valueToDec.getBytes());

        String clear = new String(Base64.encodeBase64(decValue));

        return clear;
    }

}

The error thrown is:

javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher

Any ideas?

54 69 6D
  • 674
  • 8
  • 17
Adrian B.
  • 4,333
  • 1
  • 24
  • 38

2 Answers2

2

thanks for the good question, answer and comments. I did minor changes to the code and now it works like a charm for me.

import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import android.util.Base64;

public class AESTest {

    public enum Mode {
        CBC, ECB, CFB, OFB, PCBC
    };

    public enum Padding {
        NoPadding, PKCS5Padding, PKCS7Padding, ISO10126d2Padding, X932Padding, ISO7816d4Padding, ZeroBytePadding
    }

    private static final String ALGORITHM = "AES";

    private static final byte[] keyValue ="myKey".getBytes();


    String decrypt(String valueToDec, Mode modeOption,
            Padding paddingOption) throws GeneralSecurityException {



        byte[] decodeBase64 = Base64.decode(valueToDec.getBytes(),0);

        Key key = new SecretKeySpec(keyValue, ALGORITHM); 
        Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); 
        c.init(Cipher.DECRYPT_MODE, key); 
        byte[] encValue = c.doFinal(decodeBase64); 
        return new String(encValue).trim();

    }

}

Then, I used the class in my Android Activity as follows:

AESTest aes=new AESTest();
String decrypted = aes.decrypt(myCipheredText,Mode.ECB,Padding.NoPadding);

Note that the secret in python code and mykey in java code are tthe same.

mtanha
  • 39
  • 4
1

You are padding with form-feed characters (\f). I'm not aware of a standard padding scheme that does this. As a result, I suggest you opt for NoPadding on the Java side and be prepared to strip \f characters from the plaintext you get after decryption.

Since you are able to decrypt a non-padded plaintext then it demonstrates you have the same key material on both sides (which is a common issue that I'm glad we can strike off the list).

Reading the Python documentation, it looks like ECB-mode is chosen by default. So make sure you use this on the Java side.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • You are right, NoPadding. The main problem with my test code, is the order of encodeBase64 and decrypted. I just realized that, when I tried your suggestion. The correct working code: byte[] decodeBase64 = Base64.decodeBase64(valueToDec.getBytes()); Key key = new SecretKeySpec(keyValue, ALGORITHM); Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); c.init(Cipher.DECRYPT_MODE, key); byte[] encValue = c.doFinal(decodeBase64); return new String(encValue).trim(); – Adrian B. Nov 04 '13 at 12:41