pawpaw
pawpaw

Reputation: 145

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

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 ;)

Upvotes: 1

Views: 18560

Answers (2)

Jim Flood
Jim Flood

Reputation: 8467

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.

Upvotes: 4

Henry
Henry

Reputation: 43738

The result of encryption is binary data. In most cases it cannot be interpreted as a valid string encoding. So the call to new String(encrypted) will most likely distort the encrypted bytes and after doing packet.getBytes() you end up with a byte array with different content.

The decryption now fails because the cypher text has been changed. The padding bytes are not correctly recovered and cannot be removed.

To fix that, don't convert the cypher text to a string, keep the byte array.

Upvotes: 0

Related Questions