IllegalBlockSizeException trying to decrypt string

3.7k views Asked by At

I'm using AES 128 bit encryption on JRE 1.6.

I keep getting this exception trying to decrypt strings generated by encrypt():

2014-11-19 14:40:10.831     28 javax.crypto.IllegalBlockSizeException: Input length (with padding) not multiple of 16 bytes
2014-11-19 14:40:10.834     28  at com.ibm.crypto.provider.AESCipher.a(Unknown Source)
2014-11-19 14:40:10.836     28  at com.ibm.crypto.provider.AESCipher.engineDoFinal(Unknown Source)
2014-11-19 14:40:10.837     28  at com.ibm.crypto.provider.AESCipher.engineDoFinal(Unknown Source)
2014-11-19 14:40:10.839     28  at javax.crypto.Cipher.doFinal(Unknown Source)
2014-11-19 14:40:10.841     28  at com.axa.oe.mongo.Security.decrypt(Security.java:72)
2014-11-19 14:40:10.843     28  at com.axa.oe.mongo.MongoSearch.evaluate(MongoSearch.java:62)
2014-11-19 14:40:10.844     28  at com.ibm.broker.javacompute.MbRuntimeJavaComputeNode.evaluate(MbRuntimeJavaComputeNode.java:265)
2014-11-19 14:40:10.846     28  at com.ibm.broker.plugin.MbNode.evaluate(MbNode.java:1480)

I noticed the encrypted strings are 24 bytes long, wtf? E.G. "fb/8asoHS/ShyCDV46t/Aw=="

Shouldn't they be 16 bytes? Anyway not sure if this is the problem.

Here is the source:

package com.axa.oe.mongo;

import java.security.spec.InvalidKeySpecException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import com.ibm.broker.javacompute.Base64;

public class Security {
    private static final String AES_KEY = "blah";
    private SecretKeySpec keyObj;
    private Cipher cipher;
    private IvParameterSpec ivObj;

    public Security() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException {
        // A constant IV
        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        this.ivObj = new IvParameterSpec(iv);

        byte[] key = AES_KEY.getBytes();
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        key = sha.digest(key);
        key = Arrays.copyOf(key, 16); // use only first 128 bit
        this.keyObj = new SecretKeySpec(key, "AES");

        // Create a Cipher by specifying the following parameters
        //  a. Algorithm name - here it is AES 
        //  b. Mode - here it is CBC mode 
        //  c. Padding - e.g. PKCS7 or PKCS5
        this.cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    }

    public String encrypt(String strDataToEncrypt) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
        String strCipherText = new String();

        this.cipher.init(Cipher.ENCRYPT_MODE, this.keyObj, this.ivObj);

        // Encrypt the Data 
        //  a. Declare / Initialize the Data. Here the data is of type String 
        //  b. Convert the Input Text to Bytes 
        //  c. Encrypt the bytes using doFinal method
        byte[] byteDataToEncrypt = strDataToEncrypt.getBytes();

        byte[] byteCipherText = this.cipher.doFinal(byteDataToEncrypt);

        // b64 is done differently on Android
        strCipherText = Base64.encode(byteCipherText);

        return strCipherText;
    }

    public String decrypt(String strCipherText) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
        String strDecryptedText = new String();

        // Initialize the Cipher for Encryption
        this.cipher.init(Cipher.DECRYPT_MODE, this.keyObj, this.ivObj);

        // Decrypt the Data
        //  a. Initialize a new instance of Cipher for Decryption (normally don't reuse the same object)
        //     Be sure to obtain the same IV bytes for CBC mode.
        //  b. Decrypt the cipher bytes using doFinal method
        byte[] byteDecryptedText = this.cipher.doFinal(strCipherText.getBytes());
        strDecryptedText = new String(byteDecryptedText);

        return strDecryptedText;
    }
}

I'm using a global key because the application does not use sessions or logins. Also notice the IV is a constant byte array. My version of JCE is a bit out of date, I am trying to upgrade it but currently mired in bureaucracy, so I have to make due...

Help greatly appreciated!

1

There are 1 answers

1
OtherDevOpsGene On BEST ANSWER

A string that looks like fb/8asoHS/ShyCDV46t/Aw== is a Base64 encoded representation of the cipher text byte array.

The key started out 16 bytes long. Base64 encoding increases the length to 4/3 because it is using fewer characters to represent the bytes. 16 * 4/3 = 22. But Base64 needs to convert 3 bytes at a time, so it pads the bytes to be a multiple of 3, so 16 -> 18 * 4/3 = 24. The equals signs at the end are a typical artifact of this padding in Base64.

You are Base64 encoding the cipher text after you encrypt. You need to Base64 decode the cipher text before you decrypt.

Probably something like:

public String decrypt(String strCipherText) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
    String strDecryptedText = new String();

    // Initialize the Cipher for Encryption
    this.cipher.init(Cipher.DECRYPT_MODE, this.keyObj, this.ivObj);

    // Decode the Base64 text
    byte[] cipherBytes = Base64.decode(strCipherText);

    // Decrypt the Data
    //  a. Initialize a new instance of Cipher for Decryption (normally don't reuse the same object)
    //     Be sure to obtain the same IV bytes for CBC mode.
    //  b. Decrypt the cipher bytes using doFinal method
    byte[] byteDecryptedText = this.cipher.doFinal(cipherBytes);
    strDecryptedText = new String(byteDecryptedText);

    return strDecryptedText;
}