2

I am facing the following issue while working with Java cryptography.

error decrjavax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

I have checked all possible answers, but couldn't find the exact reason behind this.

One observation that when i use AES/CBC/NoPadding in place of AES/CBC/PKCS5Padding, i can execute it successfully.

here is my code snippet.

package demo;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;


public class TestEncryption {
    private static final int BUFFER_SIZE = 32;
    private static final int KEY_ITERATIONS = 65535;
    private static final int DEFAULT_KEY_BITS = 128;

    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
    private static final String TRANSFORMATION = "AES";
    private static final String PBKDF_2_WITH_HMAC_SHA_256 = "PBKDF2WithHmacSHA256";
    private static final int IV_SIZE = 16;

    private final Cipher ecipher;
    private final Cipher dcipher;
    private SecretKey secretKey;

    /**
     * Initialize the ciphers using the given key.
     * @param key
     * @param keyBits
     */
    public TestEncryption(String key, int keyBits) {
        byte[] salt = new byte[8];

        if (key.length() < 8) {
            throw new IllegalArgumentException("key must contain 8 characters or more");
        }

        for (int i = 0; i < 8; i = i + 1) {
            salt[i] = ((byte) key.charAt(i));
        }

        char[] password = key.toCharArray();

        int keyLength = DEFAULT_KEY_BITS;

        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_2_WITH_HMAC_SHA_256);

            if (keyBits == 256) {
                keyLength = 256;
            }

            KeySpec spec = new PBEKeySpec(password, salt, KEY_ITERATIONS, keyLength);
            secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), TRANSFORMATION);
            ecipher = Cipher.getInstance(ALGORITHM);
            dcipher = Cipher.getInstance(ALGORITHM);
        } catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException e) {

            throw new RuntimeException("Failed to initialize encryption.", e);
        }

    }

    public void encryptFile(File src, File dest){
        try {
            InputStream inputStream = new FileInputStream(src);
            OutputStream outputStream = new FileOutputStream(dest);
            CipherOutputStream cipherOutputStream= new CipherOutputStream(outputStream, ecipher);

            // Generating IV.
            byte[] iv = new byte[IV_SIZE];
            SecureRandom random = new SecureRandom();
            random.nextBytes(iv);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

            // First write the IV at the beginning of the encrypted file.
            outputStream.write(iv, 0, IV_SIZE);

            System.out.println("key " + secretKey);
            // Initialize cipher with IV
            ecipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            // Encrypt input file and write in to output
            while ((bytesRead = inputStream.read(buffer)) > 0) {
                cipherOutputStream.write(buffer, 0, bytesRead);
            }

        } catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {

            System.out.println("error encryption" + e.getMessage());
            e.printStackTrace();
        }
    }

    public void decryptFile(File srcFile, File destFile) {
        try (
                InputStream is = new FileInputStream(srcFile);
                OutputStream out = new FileOutputStream(destFile);
                CipherInputStream cis = new CipherInputStream(is, dcipher)
        ) {
            // Extract IV
            byte[] iv = new byte[IV_SIZE];
            is.read(iv, 0, IV_SIZE);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

            // Initialize cypher with IV
            dcipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = cis.read(buffer)) > 0) {
                out.write(buffer, 0, bytesRead);
            }
        } catch ( InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {

            System.out.println("error decr" + e.getMessage());
            e.printStackTrace();
        }
    }
}

package demo;

import java.io.*;

public class Client {

    public static void main(String [] args){
        File tempFile =null, src = null, dest = null;

        try {
            tempFile = new File("temp.txt");
            src = new File("C:\\Users\\x\\Desktop\\test.txt");
            dest = new File("C:\\Users\\x\\Desktop\\out.txt");
            TestEncryption encryption = new TestEncryption("helloworld", 256);
            encryption.encryptFile(src, tempFile);
            encryption.decryptFile(tempFile, dest);
        }
        finally {
            tempFile.delete();
            //src.delete();
            //dest.delete();
        }
    }
}

learner
  • 332
  • 2
  • 6
  • 22
  • Before encrypting and decrypting print out the key bytes and iv bytes (e.g. in hex format) to see if your parameters are correct. because if key or IV are different decryption can not work. – Robert Jul 10 '20 at 09:52
  • Hi, I checked the key and IV in hex before encryption and after encryption and found both of them are same. Is there anything i am missing. Thanks for your response. I have edited and added my client file too in the question, – learner Jul 10 '20 at 11:13
  • One error: When decrypting you use `is.read(iv, 0, IV_SIZE);` which can read IV_SIZE bytes but does not have to. If you are on Java9+ use `readNBytes(..)` instead. And you have a security problem because you use use the key as salt. The salt has to be random like the IV! – Robert Jul 10 '20 at 11:22
  • okay. thanks for your suggestion, I am using java 8. i will change the salt to random as well. Is there any alternative in java 8 for "is.read(iv, 0, IV_SIZE)"? – learner Jul 10 '20 at 11:43
  • i have made the salt to random as well. But still the same error. – learner Jul 10 '20 at 11:46
  • Is there any other things that i am missing? – learner Jul 10 '20 at 12:40

2 Answers2

4

Your error is the way to use your streams when encrypting:

For a CipherOutputStream it is essential to be closed at the end because only when it is closed the final padding can be written.

In your code however the cipherOutputStream instance is never closed. hence the padding is never written to the encrypted file.

Of course when decrypting the file there is no padding where a padding should be and you are getting the BadPaddingException.

Therefore you should change the encyrption to this:

public void encryptFile(File src, File dest) {
    try (InputStream inputStream = new FileInputStream(src);
            OutputStream outputStream = new FileOutputStream(dest)) {

        try (CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, ecipher)) {

            // Generating IV.
            byte[] iv = new byte[IV_SIZE];
            SecureRandom random = new SecureRandom();
            random.nextBytes(iv);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

            // First write the IV at the beginning of the encrypted file.
            outputStream.write(iv, 0, IV_SIZE);

            System.out.println("key 0x" + new BigInteger(1, secretKey.getEncoded()).toString(16));
            // Initialize cipher with IV
            ecipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            // Encrypt input file and write in to output
            while ((bytesRead = inputStream.read(buffer)) >= 0) {
                cipherOutputStream.write(buffer, 0, bytesRead);
            }
        }

    } catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {

        System.out.println("error encryption" + e.getMessage());
        e.printStackTrace();
    }
}

public void decryptFile(File srcFile, File destFile) {
    try (InputStream is = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile)) {
        try (CipherInputStream cis = new CipherInputStream(is, dcipher)) {
            // Extract IV
            byte[] iv = is.readNBytes(IV_SIZE);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

            // Initialize cypher with IV
            dcipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = cis.read(buffer)) >= 0) {
                out.write(buffer, 0, bytesRead);
            }
        }
    } catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {

        System.out.println("error decr" + e.getMessage());
        e.printStackTrace();
    }
}
Robert
  • 39,162
  • 17
  • 99
  • 152
  • Thanks a lot Robert. It helps a lot.. I would be very much grateful to you if you kindly help me with another issues I am facing "java.io.IOException: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher ". Can you please give me some suggestions what might goes wrong and when can I get this error? I checked many posts but couldn't find. So – learner Jul 11 '20 at 06:11
  • 1
    @user10237300 sounds to me again like wrong closing and flushing of streams. I have updated my answer to include both decrypt and encrypt method with small modifications. – Robert Jul 11 '20 at 11:20
  • Okay... I will check out if I am missing anything on the wrapper code.. I appreciate your help – learner Jul 11 '20 at 12:49
-2

There is another reason that probably the content which you need to description, but the arguments is null when passed to the mathod, it will be throw you a badpaddedException as well.