35

I have following program for encrypting data.

import java.security.Key;

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

import org.apache.commons.codec.binary.Base64;

public class Test {

    private static final String ALGORITHM = "AES";
    private static final byte[] keyValue = "ADBSJHJS12547896".getBytes();

    public static void main(String args[]) throws Exception {
        String encriptValue = encrypt("dude5");
        decrypt(encriptValue);

    }

    /**
     * @param args
     * @throws Exception
     */

    public static String encrypt(String valueToEnc) throws Exception {

        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);

        System.out.println("valueToEnc.getBytes().length "+valueToEnc.getBytes().length);
        byte[] encValue = c.doFinal(valueToEnc.getBytes());
        System.out.println("encValue length" + encValue.length);
        byte[] encryptedByteValue = new Base64().encode(encValue);
        String encryptedValue = encryptedByteValue.toString();
        System.out.println("encryptedValue " + encryptedValue);

        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[] enctVal = c.doFinal(encryptedValue.getBytes());
        System.out.println("enctVal length " + enctVal.length);

        byte[] decordedValue = new Base64().decode(enctVal);

        return decordedValue.toString();
    }

    private static Key generateKey() throws Exception {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);
        return key;
    }

}

Here I am getting the following out put with exception?

valueToEnc.getBytes().length 5
encValue length16
encryptedValue [B@aa9835
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)

Can some one explain me the cause? Why its only saying when decrypting that length should be 16. Doesn't it convert to 16 as like encrypting with the doFinal method.

And as the exception says "how to decrypting without padded cipher?"

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Harshana
  • 7,297
  • 25
  • 99
  • 173

6 Answers6

66

Your Order for encrypt: getBytes, encrypt, encode, toString
Your Order for decrypt(Wrong*): getBytes, decrypt, decode, toString

Two problems:

  1. As someone already mentioned you should reverse the order of operations for decryption. You are not doing that.
  2. encrypt gives you 16 bytes, encode 24 bytes, but toString gives 106 bytes. Something to do with invalid chars taking up additional space.

Note: Also, you don't need to call generateKey() twice.

Fix problem #1 by using the reverse order for decryption.
Correct order for decrypt: getBytes, decode, decrypt, toString

Fix problem #2 by replacing xxx.toString() with new String(xxx). Do this in both the encrypt and decrypt functions.

Your decrypt should look like this:

c.init(Cipher.DECRYPT_MODE, key)
val decodedValue = new Base64().decode(encryptedValue.getBytes())
val decryptedVal = c.doFinal(decodedValue)
return new String(decryptedVal)

This should give you back "dude5"

cryptonkid
  • 924
  • 1
  • 17
  • 25
Babu Srinivasan
  • 2,339
  • 23
  • 24
  • 1
    at the top of your answer you give decrypt order as: getBytes, decrypt, decode, toString. Then later you give correct order as: getBytes, decode, decrypt, toString. I believe the 2nd ordering is correct. – Magnus Aug 28 '12 at 17:11
  • 4
    Magnus, at the top of my answer, when I mentioned "Your Order ...", that is the order in which OP was performing the sequence. The "Your" refers to OP. That is not 'my' order. – Babu Srinivasan Sep 11 '12 at 07:39
  • 4
    It is also important to note that one should always use a specific Charset when creating String from byte[] and vice versa: e.g. `new String(bytes, "UTF-8")` and `string.getBytes("UTF-8")`. This ensures that if encryption and decryption are executed on different systems with different system charsets, this won't result in a failure. – Artjom B. Apr 24 '15 at 19:52
  • Thanks! I was getting the same error. Encrypting and writing to an external file. Once read back and decrypt, I wasn't following the correct order. I missed the getBytes() part from String. – yonikawa Jul 25 '22 at 20:07
5

The line

String encryptedValue = encryptedByteValue.toString();

is the problem. The type of encryptedByteValue is byte[] and calling toString on it isn't what you want to do there. Instead try

String encryptedValue = Base64.getEncoder().encodeToString(encValue);

Then use Base64.decodeBase64(encryptedValue) in decrypt. You must do that prior to attempting to decrypt though. You must undo the operations in the reverse order of the encrypt method.

Singhak
  • 8,508
  • 2
  • 31
  • 34
laz
  • 28,320
  • 5
  • 53
  • 50
  • 1
    its just add there to return a String from the method..also in Base64 there is no such method encodeToString – Harshana Oct 18 '10 at 01:22
  • Regarding the toString, calling that method on an Array is almost never what you want to do. It returns the address in memory of the object, not a useful String representation. Regarding Base64, aren't you using this? http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html See the method here: http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html#encodeToString%28byte[]%29 – laz Oct 18 '10 at 03:07
  • ive put commons-codec-1.2 and also try 1.3 jar but seems that method is not display know.. – Harshana Oct 18 '10 at 06:48
  • It is in commons-code 1.4 according to that Javadoc I linked to. – laz Oct 18 '10 at 13:42
  • I didn't find any method Base64.encodeToString(), but using just new String(encValue) fixed the problem for me. – Magnus Aug 28 '12 at 17:21
2

That was alright, you just needed to

1) Use new String instead of toString() since toString() doesn't return what you need here (in both cases, encryption and decryption)

2) you need to decode first since the value is encode in base64.

I came across this thread but it took sometime to find out the actual point..I am posting my code for rest of the people who come across this issue.

public abstract class EncryptionDecryption {
static  byte[]  key = "!@#$!@#$%^&**&^%".getBytes();
final static String algorithm="AES";

public static String encrypt(String data){

    byte[] dataToSend = data.getBytes();
    Cipher c = null;
    try {
        c = Cipher.getInstance(algorithm);
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    SecretKeySpec k =  new SecretKeySpec(key, algorithm);
    try {
        c.init(Cipher.ENCRYPT_MODE, k);
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    byte[] encryptedData = "".getBytes();
    try {
        encryptedData = c.doFinal(dataToSend);
    } catch (IllegalBlockSizeException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (BadPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    byte[] encryptedByteValue =    new Base64().encode(encryptedData);
    return  new String(encryptedByteValue);//.toString();
}

public static String decrypt(String data){

    byte[] encryptedData  = new Base64().decode(data);
    Cipher c = null;
    try {
        c = Cipher.getInstance(algorithm);
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    SecretKeySpec k =
            new SecretKeySpec(key, algorithm);
    try {
        c.init(Cipher.DECRYPT_MODE, k);
    } catch (InvalidKeyException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    byte[] decrypted = null;
    try {
        decrypted = c.doFinal(encryptedData);
    } catch (IllegalBlockSizeException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (BadPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return new String(decrypted);
}

public static void main(String[] args){
    String password=EncryptionDecryption.encrypt("password123");
    System.out.println(password);
    System.out.println(EncryptionDecryption.decrypt(password));
}
}
Danyal Sandeelo
  • 12,196
  • 10
  • 47
  • 78
2

Fundamentally, there is an asymmetry between your encrypt function and your decrypt function. When you encrypt you perform an AES encrypt and then a base64 encode, when you decrypt you don't first undo the base64 encoding step.

I think that there's something wrong with your base64 encoding as well as [ shouldn't appear in a base64 encoded string.

Looking at the documentation for org.apache.commons.codec.binary.Base64 you should be able to do this on encode:

String encryptedValue = Base64.encodeBase64String(encValue);

and this on decode:

byte[] encValue = Base64.decodeBase64(encryptedValue);
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • @Harshana: What's this, then? http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html#encodeBase64String%28byte[]%29 – CB Bailey Oct 18 '10 at 05:16
2

Where are you getting a version of apache codec that has encodeToString or encodeBase64String?

I downloaded 1.5 from the apache site and while it says in the documentation that these methods exist, they don't show up when you do code completion and they create an unknown method when you provide them.

I was able to do:

byte raw[] = md.digest(); //step 4
byte hashBytes[] = Base64.encodeBase64(raw); //step 5
StringBuffer buffer = new StringBuffer();
for( int i=0; i<hashBytes.length; i++ )
    buffer.append(hashBytes[i]);
return buffer.toString(); //step 6

And then the string that I obtained was very long, BUT it decrypted correctly.

I don't think this is the "right" way to do things, but can't find the methods that the documentation says are there.

1

I have replaces line in example:

String encryptedValue = encryptedByteValue.toString();

with next one:

String encryptedValue = new String(encryptedByteValue);

All works fine!

Alex
  • 11
  • 1