SlumpA SlumpA - 26 days ago 22
Java Question

javax.crypto.BadPaddingException: Given final block not properly padded in CipherInputStream

This question was probably asked already, but since I'm having a hard time understanding cryptography, and especially key handling, I'm not sure how to handle this exception, nor where / how to find the solution.

I'm trying to write and read objects to and from an encrypted file.
For that purpose I've wrapped a CipherInputStream under a DataInputStream.

I'm getting the BadPadding exception, which probably means I'm doing something wrong with the key.

Please help me.

Main.java:

package main;

import java.util.Arrays;

import io.Io;

public class Main {
static final String FIRST_TEXT_TO_CRYPTO = "First text";
static final boolean BOOLEAN_CRYPTO = true;
static final String SECOND_TEXT_TO_CRYPTO = "First text";
static final String KEY = "key123";

public static void main(String[] args) {
Io.encryptAndWriteAll(Arrays.asList(new Stuff(FIRST_TEXT_TO_CRYPTO, BOOLEAN_CRYPTO, SECOND_TEXT_TO_CRYPTO)),
KEY);

for (Stuff stuff : Io.readAndDecryptAllFromLocation(KEY)) {
System.out.println(stuff);
}
}
}


Stuff.java

package main;

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;

public class Stuff {
SimpleStringProperty firstString = new SimpleStringProperty(), secondString = new SimpleStringProperty();
SimpleBooleanProperty bool = new SimpleBooleanProperty();

public Stuff(String firstString, boolean bool, String secondString) {
this.firstString.set(firstString);
this.secondString.set(secondString);
this.bool.set(bool);
}

@Override
public String toString() {
return String.format("FirstString: %s; Boolean: %B; SecondString: %s", getFirstString(), isBool(),
getSecondString());
}

public final SimpleStringProperty firstStringProperty() {
return this.firstString;
}

public final String getFirstString() {
return this.firstStringProperty().get();
}

public final void setFirstString(final String firstString) {
this.firstStringProperty().set(firstString);
}

public final SimpleStringProperty secondStringProperty() {
return this.secondString;
}

public final String getSecondString() {
return this.secondStringProperty().get();
}

public final void setSecondString(final String secondString) {
this.secondStringProperty().set(secondString);
}

public final SimpleBooleanProperty boolProperty() {
return this.bool;
}

public final boolean isBool() {
return this.boolProperty().get();
}

public final void setBool(final boolean bool) {
this.boolProperty().set(bool);
}

}


Io.java:

package io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;

import main.Stuff;

public class Io {
private static final File FILE = new File("file.file");

public static void encryptAndWriteAll(List<Stuff> stuffs, String key) {
try (FileOutputStream fos = new FileOutputStream(FILE);
BufferedOutputStream bos = new BufferedOutputStream(fos);
CipherOutputStream cos = new CipherOutputStream(bos, new Crypto(key).getEncryptionCipher());
DataOutputStream dos = new DataOutputStream(cos)) {
for (Stuff stuff : stuffs) {
dos.writeUTF(stuff.getFirstString());
dos.writeBoolean(stuff.isBool());
dos.writeUTF(stuff.getSecondString());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static List<Stuff> readAndDecryptAllFromLocation(String key) {
try (FileInputStream fis = new FileInputStream(FILE);
BufferedInputStream bis = new BufferedInputStream(fis);
CipherInputStream cis = new CipherInputStream(bis, new Crypto(key).getDecryptionCipher());
DataInputStream dis = new DataInputStream(cis)) {
ArrayList<Stuff> stuffs = new ArrayList<>();
try {
for (;;)
// The next line throws
// ("at io.Io.readAndDecryptAllFromLocation(Io.java:52)")
stuffs.add(new Stuff(dis.readUTF(), dis.readBoolean(), dis.readUTF()));
} catch (EOFException e) {
System.out.println("EOF");
e.printStackTrace();
} catch (UTFDataFormatException e) {
e.printStackTrace();
}
return stuffs;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}


Crypto.java:

package io;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

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

public class Crypto {

private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static final String ALGORITHM = "AES";
private byte[] paddedKey;

public Crypto(String key) {
paddedKey = addPaddingToKey(key);
}

private byte[] addPaddingToKey(String key) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return Arrays.copyOf(digest.digest(key.getBytes()), 16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}

private Key getKey() {
return new SecretKeySpec(paddedKey, ALGORITHM);
}

public Cipher getEncryptionCipher() {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, getKey());
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}

public Cipher getDecryptionCipher() {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, getKey());
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}

}


Exception:

java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:121)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:239)
at java.io.DataInputStream.readFully(DataInputStream.java:195)
at java.io.DataInputStream.readUTF(DataInputStream.java:609)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at io.Io.readAndDecryptAllFromLocation(Io.java:52)
at main.Main.main(Main.java:17)
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:975)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:833)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2048)
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:118)
... 6 more
Exception in thread "main" java.lang.NullPointerException
at main.Main.main(Main.java:17)

Answer

You are using AES/CBC, Which would need IV parameter. If you have not specified IV parameter while encrypting a random IV would be generated and used.

You can randomly generate IV and prepend it to encrypted data, since IV need not need to be secret.

Comments