18
votes

Using tomcat, I have two web-applications i.e app1 and app2. I sent url from app1 in encrypted form (using below code) to app2 . Then at app2 I decrypted this encrypted url. But am getting below exception at line 50 of decryp method.

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

Though for debugging when I try to decrypt(using same code) the encrypted url at app1, it works fine. But not able to figure out what causing this exception at app2?

Here is the code

import java.security.Key;

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

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class AESEncryptionDecryptionTest {

    private static final String ALGORITHM       = "AES";
    private static final String myEncryptionKey = "ThisIsFoundation";
    private static final String UNICODE_FORMAT  = "UTF8";

    public static String encrypt(String valueToEnc) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);  
        byte[] encValue = c.doFinal(valueToEnc.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encValue);
        return encryptedValue;
    }

    public static String decrypt(String encryptedValue) throws Exception {
         Key key = generateKey();
         Cipher c = Cipher.getInstance(ALGORITHM);
         c.init(Cipher.DECRYPT_MODE, key);
         byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
         byte[] decValue = c.doFinal(decordedValue);//////////LINE 50
         String decryptedValue = new String(decValue);
         return decryptedValue;
    }

    private static Key generateKey() throws Exception {
         byte[] keyAsBytes;
         keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
         Key key = new SecretKeySpec(keyAsBytes, ALGORITHM);
         return key;
    }

    public static void main(String[] args) throws Exception {

         String value = "password1";
         String valueEnc = AESEncryptionDecryptionTest.encrypt(value);
         String valueDec = AESEncryptionDecryptionTest.decrypt(valueEnc);

         System.out.println("Plain Text : " + value);
         System.out.println("Encrypted : " + valueEnc);
         System.out.println("Decrypted : " + valueDec);
    }

}
4
See my ans and Get some idea.[Here same question][1] [1]: stackoverflow.com/questions/17234359/… hope that will help. - Kishan Bheemajiyani

4 Answers

11
votes

Works on my machine. Does it help if you use `UNICODE_FORMAT' in every instance where you transform bytes to Strings and vice versa? This line could be a problem:

byte[] encValue = c.doFinal(valueToEnc.getBytes()); 

should be

byte[] encValue = c.doFinal(valueToEnc.getBytes(UNICODE_FORMAT));

Anyway, if you use "AES" as the algorithm and you use the JCE, the algorithm actually being used will be "AES/ECB/PKCS5Padding". Unless you are 100% sure about what you are doing, ECB shouldn't be used for anything. I'd recommend to always specify the algorithm explicitly in order to avoid such confusion. "AES/CBC/PKCS5Padding" would be a good choice. But watch out, with any reasonable algorithm you will also have to provide and manage an IV.

Using an ECB cipher is even less desirable in the context of encrypting passwords, which is what you seem to be doing with your encryption if I interpret your example correctly. You should use password-based encryption as specified in PKCS#5 for that purpose, in Java this is provided for you in SecretKeyFactory. Be sure to use "PBKDF2WithHmacSHA1" with a high enough iteration count (anything ranging from ~ 5-20 000, depends on your target machine) for using passwords to derive symmetric keys from them.

The same technique can be used if it is actually password storage instead of password encryption what you are trying to achieve.

-1
votes

I was seeing this error (with the password getting through just fine like you say here) when double-decrypting a value. Be sure to check to see if you are doing the decrypting more than once. (mine was multiple of 8 in the error, but I was using a different scheme there...) In my case I was decrypting when reading a file and then decrypting again when filling out a field. (desktop app)

-1
votes

I would suggest instead of working with Strings work with byte[] itself. I am guessing some bytes are modified when you convert it into a String. Following code works for me -

public static final String ENC_KEY = "abcdefghijklmnop";
public static final String DATA = "Hello World";

public static void test(){

    try {
        Cipher c = Cipher.getInstance("AES");

        SecretKeySpec secretKeySpec = new SecretKeySpec(ENC_KEY.getBytes("UTF-8"), "AES");

        c.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] encBytes = c.doFinal(DATA.getBytes("UTF-8"));
        String encStr =  new String(encBytes, "UTF-8");
        System.out.println("Encrypted String: " + encStr);

        c.init(Cipher.DECRYPT_MODE, secretKeySpec);
        String decStr = new String(c.doFinal(encBytes),"UTF-8");
        System.out.println("Decrypted String: " + decStr);

    } catch (Exception ex) {
        System.out.println("Error in encrypting data");
        ex.printStackTrace();
    }
}

but if you change it to -

public static void test(){

    try {
        Cipher c = Cipher.getInstance("AES");

        SecretKeySpec secretKeySpec = new SecretKeySpec(ENC_KEY.getBytes("UTF-8"), "AES");

        c.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] encBytes = c.doFinal(DATA.getBytes("UTF-8"));
        String encStr =  new String(encBytes, "UTF-8");
        System.out.println("Encrypted String: " + encStr);

        c.init(Cipher.DECRYPT_MODE, secretKeySpec);
        String decStr = new String(c.doFinal(encStr.getBytes("UTF-8")),"UTF-8");
        System.out.println("Decrypted String: " + decStr);

    } catch (Exception ex) {
        System.out.println("Error in encrypting data");
        ex.printStackTrace();
    }
}

You will get

javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847) at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) at javax.crypto.Cipher.doFinal(Cipher.java:2164) at com.osfg.HelloWorld.test(HelloWorld.java:38) at com.osfg.HelloWorld.main(HelloWorld.java:22)

Notice

String decStr = new String(c.doFinal(encBytes),"UTF-8");
VRS
String decStr = new String(c.doFinal(encStr.getBytes("UTF-8")),"UTF-8");
-3
votes

This error indicates that you used choice combination requires 16 characters source text only. If you want to encrypt a password, you can truncate or pad the original password to 16-char for encryption, and trim after decryption. This way must limit real password not longer than 16-char, but you may apply longer used password to confuse those who should not know your password.