Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IllegalBlockSizeException when trying to encrypt and decrypt a string with AES

I have a hardcoded key with which I want to encrypt a string before storing it in SharedPreferences. This is the code I have so far:

public class TokenEncryptor {

    private final static String TOKEN_KEY = "91a29fa7w46d8x41";

    public static String encrypt(String plain) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
            SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
            return new String(cipher.doFinal(plain.getBytes()));
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    public static String decrypt(String encoded) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
            SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
            cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
            return new String(cipher.doFinal(encoded.getBytes()));
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }
}

It seems to be catching an exception at the end of decrypt method:

javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

Can someone point me in the right direction? I have a feeling I'm doing something wrong instantiating IvParameterSpec.

like image 333
Oleksiy Avatar asked May 21 '15 20:05

Oleksiy


2 Answers

When you encrypt a string with AES, you get an array of bytes back. Trying to convert those bytes directly to a string (new String(cipher.doFinal(plaintextBytes))) will cause all sorts of problems. If you require the output from your encryption method to be a string, then use Base64 rather than attempting a direct conversion. In your decryption method, convert the Base64 string back into a byte array before decrypting the byte array.

Also, do not use getBytes() since the output depends on the system defaults. Use getBytes("utf-8") or whatever. That eliminates ambiguity.

like image 126
rossum Avatar answered Oct 03 '22 01:10

rossum


Just in case anyone is interested (or feels too lazy to do their research), here is the the result code for AES-256 encryption and decryption that I put together, with help from the accepted answer and comments:

public class TokenEncryptor {

    private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw";

    public static String encrypt(String plain) {
        try {
            byte[] iv = new byte[16];
            new SecureRandom().nextBytes(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
            byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8"));
            byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
            return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    public static String decrypt(String encoded) {
        try {
            byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
            byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
            byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
            return new String(cipher.doFinal(cipherText), "utf-8");
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    private static byte[] getCombinedArray(byte[] one, byte[] two) {
        byte[] combined = new byte[one.length + two.length];
        for (int i = 0; i < combined.length; ++i) {
            combined[i] = i < one.length ? one[i] : two[i - one.length];
        }
        return combined;
    }

}
like image 37
Oleksiy Avatar answered Oct 03 '22 01:10

Oleksiy