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

35,242

Solution 1

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 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.

Solution 2

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 "";
}

Solution 3

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..

Share:
35,242
lazypig
Author by

lazypig

Updated on October 15, 2020

Comments

  • lazypig
    lazypig over 3 years

    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?

    • Maarten Bodewes
      Maarten Bodewes over 11 years
      Note that if you receive the String "~9?8?m???=?T?G" from a server, instead of a base 64 encoded string or just plain bytes (as received from an InputStream) then the server does not produce valid cipher text. Those question marks are unprintable characters, and when those are present you are likely loosing information.
    • lazypig
      lazypig over 11 years
      Thanks @owlstead. You bring up a good point. The ~9?8?m???=?T?G was originally created with the String 4 and we were hoping to decrypt this and retrieve 4, but haven't been successful. I was hoping to get a basic decryption working and then build off from there. This is first step and I haven't gotten through it. I'll respond as I find out more from a few more tries following your suggestion.
    • Maarten Bodewes
      Maarten Bodewes over 11 years
      You have to encode the results on the server using base 64 for the code to work, if you cannot directly transfer bytes.
  • lazypig
    lazypig over 11 years
    Thank you for replying first. Hmmm are you saying that I shouldn't be encoding into a base 64 byte array? But don't I need to pass in a byte array for the decryption? What do you suggest for that? I'm just not understanding the exception and what I can do to even get it to return even a blank or something rather than throwing the exception. BadPaddingException was something that I also ran into. Do you have any other suggestions @owlstead?
  • Maarten Bodewes
    Maarten Bodewes over 11 years
    Base 64 is not a byte array. It is an encoding in characters of the byte array. So you need to decode the string you are getting into a byte array, and decrypt that. BadPaddingException is what you run into when the last block of the ciphertext does not decode into a plain text block with valid padding. This happens when the bytes of the last ciphertext block are incorrect, or when the key is incorrect. In other words, it just tells you something is wrong (which is not very helpful in itself :P ).
  • Jayesh
    Jayesh about 10 years
    Which Base64 class you are using ?? I get error on that line the method decodeBase64(byte[]) is undefined for the Base64
  • mochadwi
    mochadwi about 5 years
    stackoverflow.com/a/20417874/3763032, or you can use this workaround. for full de/encoding your de/encrypted string first