Java AES String decrypting "given final block not properly padded"

pawpaw picture pawpaw · Jun 5, 2014 · Viewed 18.4k times · Source

For all haters, I READ MANY topics like this one, and non of them was helpful.

eg. here javax.crypto.BadPaddingException: Given final block not properly padded error while decryption or here Given final block not properly padded

I want to encrypt and then decrypt Strings. Read many topics about "Given final block not properly padded" exception, but non of these solutions worked.

My Class:

package aes;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JOptionPane;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

public class EncryptionExample {

private static SecretKeySpec    key;
private static IvParameterSpec  ivSpec;
private static Cipher           cipher; 
private static byte[]           keyBytes;
private static byte[]           ivBytes;
private static int              enc_len;

public static void generateKey() throws Exception
        {
        
            String complex = new String ("9#82jdkeo!2DcASg");
            keyBytes = complex.getBytes();
            key = new SecretKeySpec(keyBytes, "AES");

            complex = new String("@o9kjbhylK8(kJh7"); //just some randoms, for now
            ivBytes = complex.getBytes();
            ivSpec = new IvParameterSpec(ivBytes);

            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        }
        
        public static String encrypt(String packet) throws Exception
        {
            byte[] packet2 = packet.getBytes();
            cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
            byte[] encrypted = new byte[cipher.getOutputSize(packet2.length)];
            enc_len = cipher.update(packet2, 0, packet2.length, encrypted, 0);
            enc_len += cipher.doFinal(encrypted, enc_len);
            
            return packet = new String(encrypted);
        }
        
        public static String decrypt(String packet) throws Exception
        {
            byte[] packet2 = packet.getBytes();
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
            byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
            int dec_len = cipher.update(packet2, 0, enc_len, decrypted, 0);
HERE EXCEPTION>>>>> dec_len += cipher.doFinal(decrypted, dec_len);  <<<<<<<<<
            
            return packet = new String(decrypted);
        }
        
        
        // and display the results
    public static void main (String[] args) throws Exception 
    {
        
          // get the text to encrypt
        generateKey();
        String inputText = JOptionPane.showInputDialog("Input your message: ");
        
        String encrypted = encrypt(inputText);
        String decrypted = decrypt(encrypted);            
                
        JOptionPane.showMessageDialog(JOptionPane.getRootFrame(),
                    "Encrypted:  " + new String(encrypted) + "\n"
                      +  "Decrypted: : " + new String(decrypted));
          .exit(0);
    }
}

The thing is, when I decrypt strings (about 4/10 of shots), I get that exception:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:479)
at javax.crypto.Cipher.doFinal(Cipher.java:2068)
at aes.EncryptionExample.deszyfrujBez(EncryptionExample.java:HERE tag)
at aes.EncryptionExample.main(EncryptionExample.java:Main starting)

Does anybody know what to change here (key? *.doFinal() method?) to make it work?

@ for those curious - methods have to be static, as this is a part of something bigger ;)

Answer

Jim Flood picture Jim Flood · Jun 6, 2014

When you use byte[] packet2 = packet.getBytes() you are converting the string based on the default encoding, which could be UTF-8, for example. That's fine. But then you convert the ciphertext back to a string like this: return packet = new String(encrypted) and this can get you into trouble if this does not round-trip to the same byte array later in decrypt() with another byte[] packet2 = packet.getBytes().

Try this instead: return packet = new String(encrypted, "ISO-8859-1"), and byte[] packet2 = packet.getBytes("ISO-8859-1") -- it's not what I would prefer, but it should round-trip the byte arrays.