Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javax.crypto.IllegalBlockSizeException: last block incomplete in decryption - Decrypting an encrypted AES String

I am trying to decrypt the string "~9?8?m???=?T?G" that I receive from a back-end server which uses OpenSSL to encrypt the String using AES-256-CBC. There is the code block:

public static String decryptText(String textToDecrypt) {
    try {

        byte[] base64TextToDecrypt = Base64.encodeBase64(textToDecrypt.getBytes("UTF-8"));

        byte[] guid = "fjakdsjkld;asfj".getBytes("UTF-8");

        byte[] iv = new byte[16];
        System.arraycopy(guid, 0, iv, 0, guid.length);
        IvParameterSpec ips = new IvParameterSpec(iv);

        byte[] secret = DECRYPTION_SECRET_HASH.getBytes("UTF-8");
        SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // decryption pass
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ips);
        byte[] converted = cipher.doFinal(base64TextToDecrypt);
        System.out.println(new String(converted));

    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Decipher error for " + textToDecrypt, e);
    }
    return "";
}

Unfortunately, when I get to the

byte[] converted = cipher.doFinal(base64TextToDecrypt);

statement the following exception is thrown:

javax.crypto.IllegalBlockSizeException: last block incomplete in decryption

Any ideas?

like image 453
lazypig Avatar asked Nov 06 '12 23:11

lazypig


3 Answers

You should decode the string instead of encoding the platform specific representation of the string, right at the start of your method.

byte[] base64TextToDecrypt = Base64.decodeBase64(textToDecrypt);

or more precisely:

byte[] bytesToDecrypt = Base64(base64TextToDecrypt);

if you name your variables correctly.

In general, each time you (feel like you have to) use the String.getBytes(): byte[] method or the String(byte[]) constructor you are likely doing something wrong. You should first think about what you are trying to do, and specify a character-encoding if you do need to use it.

In your case, the output in the converted variable is probably character-encoded. So you you could use the following fragment:

String plainText = new String(converted, StandardCharsets.UTF_8);
System.out.println(plainText);

instead of what you have now.

like image 102
Maarten Bodewes Avatar answered Nov 03 '22 00:11

Maarten Bodewes


So thanks to @owlstead, I was able to figure out the solution. It was that I made the mistake of Base64encoding an already Base64 encoded string. The following is by code chunk.

public static String decryptText(String textToDecrypt) {
    try {
        byte[] decodedValue = Base64.decodeBase64(textToDecrypt.getBytes());

        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        IvParameterSpec ips = new IvParameterSpec(iv);

        byte[] input = textToDecrypt.getBytes();

        Cipher cipher = Cipher.getInstance(ENCRYPTION_METHOD);

        // decryption pass
        cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY, ips);
        byte[] plainText = cipher.doFinal(decodedValue);

        return new String(plainText);
    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Decipher error for " + textToDecrypt, e);
    }

    return "";
}

The corresponding encrypting is like this

public static String encryptText(String textToEncrypt) {
    try {
        byte[] guid = "1234567890123456".getBytes("UTF-8");

        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        IvParameterSpec ips = new IvParameterSpec(iv);

        // The secret key from the server needs to be converted to byte array for encryption.
        byte[] secret = ENCRYPTION_SECRET_HASH.getBytes("UTF-8");

        // we generate a AES SecretKeySpec object which contains the secret key.
        // SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");
        Cipher cipher = Cipher.getInstance(ENCRYPTION_METHOD);
        cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY, ips);

        byte[] cipherText = cipher.doFinal(textToEncrypt.getBytes());
        byte[] base64encodedSecretData = Base64.encodeBase64(cipherText);
        String secretString = new String(base64encodedSecretData);
        return secretString;
    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Encryption error for " + textToEncrypt, e);
    }
    return "";
}
like image 32
lazypig Avatar answered Nov 03 '22 01:11

lazypig


Keep the following things in mind while encrypting and decrypting strings,

ENCRYPTION:

  1. Convert string to byteArray using toByteArray(Charsets.UTF-8) and always specify the charset with UTF-8.

  2. Encrypyt the above byteArray using cipher.doFinal(byteArray).

  3. Now this is not enough, you need to do base64 encoding for that encrypted byteArray using Base64.encode(encryptedByteArray, Base64.DEFAULT)

  4. Remember this again returns byteArray , if you want to convert to string, use toString(Charsets.UTF-8) and most importantly specify the charset again as UTF-8 and then process or store it in DB as you wish.

DECRYPTION:

1.Get the encrypted string and first step while decrypting is to decode the encrypted string using base64.decode(encryptedString.toByteArray(Charsets.UTF-8), Base64.DEFAULT)

  1. Now decrypt the decoded byteArray by using cipher.dofinal(decodedByteArray).

  2. Convert the Decrypted byteArray to String using toString(Charsets.UTF-8). NOTE:Always specify the charset. This returns the original string.

I know I have not shared any code but trust me the flow is the important part while encrypting and decrypting a string..

like image 2
Sai Teja Avatar answered Nov 02 '22 23:11

Sai Teja