javax.crypto.BadPaddingException
I am working on AES algorithm, and I have this exception which I couldn't solve.
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
the exception happens in the decryption part. I initialize the key in a different place from where the decryption algorithm is
KeyGenerator kgen = KeyGenerator.getInstance("AES");//key generation for AES
kgen.init(128); // 192 and 256 bits may not be available
then I pass it with the cipher text which I read from file to the following method
public String decrypt(String message, SecretKey skey) {
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher;
byte[] original = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
System.out.println("Original string: "
+ message);
original = cipher.doFinal(message.trim().getBytes()); //here where I got the exception
String originalString = new String(original);
}
//catches
EDIT here's the encryption method.
public String encrypt(String message, SecretKey skey) {
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher;
byte[] encrypted = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
encrypted = cipher.doFinal(message.getBytes());
System.out.println("raw is " + encrypted);
} catches
return asHex(encrypted);
}
and here's the asHex method
public static String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
Here's where I read the cipher text form the file
static public String readFile(String filePath) {
StringBuilder file = new StringBuilder开发者_如何学C();
String line = null;
try {
FileReader reader = new FileReader(filePath);
BufferedReader br = new BufferedReader(reader);
if (br != null) {
line = br.readLine();
while (line != null) {
file.append(line);
// System.out.println("line is " + line);
line = br.readLine();
}
}
br.close();
reader.close();
} catch (IOException ex) {
Logger.getLogger(FileManagement.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("line is " + file.toString());
return String.valueOf(file);
}
can someone help?
Ok, so the problem is that you are converting the encrypted bytes to a hex string (using the asHex
method) but are not converting the hex string back to a byte array correctly for decryption. You can't use getBytes
.
You can use the following method to convert a hex string to a byte array:
public static byte[] fromHexString(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
and then change your decrypt method to use:
original = cipher.doFinal(fromHexString(message));
I did have a Bad Padding Exception and have not been able to find on the internet a solution to my problem. Since I found it after some hard-working hours, I give it here.
My problem was, I was reading a file on my hard drive, and encrypting it through a buffer, always calling the doFinal() method instead of update() method. So when decrypting it, I had padding errors
input = new FileInputStream(file);
output = new FileOutputStream(newFile);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, mySecretKey);
byte[] buf = new byte[1024];
count = input.read(buf);
while (count >= 0) {
output.write(cipher.update(buf, 0, count)); // HERE I WAS DOING doFinal() method
count = input.read(buf);
}
output.write(cipher.doFinal()); // AND I DID NOT HAD THIS LINE BEFORE
output.flush();
And when decrypting, with the same method, but with a Cipher init with DECRYPT_MODE
input = new FileInputStream(file);
output = new FileOutputStream(newFile);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, mySecretKey);
byte[] buf = new byte[1024];
count = input.read(buf);
while (count >= 0) {
output.write(cipher.update(buf, 0, count)); // HERE I WAS DOING doFinal() method
//AND HERE WAS THE BadPaddingExceotion -- the first pass in the while structure
count = input.read(buf);
}
output.write(cipher.doFinal()); // AND I DID NOT HAD THIS LINE BEFORE
output.flush();
With the code written, I no longer have any BadPaddingException.
I may precise that this exception only appears when the original clear file length (obtained through file.length()) is bigger than the buffer. Else, we do not need to pass several times in the while structure, and we can encrypt in one pass with a doFinal() call. That justify the random character of the exception following the size of the file you try to encrypt.
I hope you had a good reading!
I guess the expression message.trim().getBytes()
does not return the same bytes which are generated when you encrypted the message. Specially the trim()
method could delete the bytes which were added as padding in the encrypted message.
Verify that both the returned array of the doFinal()
method during the encryption and the returned array of message.trim().getBytes()
:
- got the same number of bytes (array length)
- got the same bytes in the array
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair rsaKeyPair = kpg.genKeyPair();
byte[] txt = "This is a secret message.".getBytes();
System.out.println("Original clear message: " + new String(txt));
// encrypt
Cipher cipher;
try
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
txt = cipher.doFinal(txt);
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
System.out.println("Encrypted message: " + new String(txt));
// decrypt
try
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
txt = cipher.doFinal(txt);
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
System.out.println("Decrypted message: " + new String(txt));
Here is a solution I was able to piece together using a jks keystore with RSA encryption
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;
public class Main {
public static void main(String[] args) {
byte[] txt = "This is a secret message for your own eyes only".getBytes();
byte[] encText;
try{
// Load the keystore
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = "keystorePassword".toCharArray();
java.io.FileInputStream fis = new java.io.FileInputStream("/path/to/keystore/myKeyStore.jks");
ks.load(fis, password);
fis.close();
Key rsakey = ks.getKey("mykeyalias", password);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Encrypt
Certificate cert = ks.getCertificate("mykeyalias");
try
{
cipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
encText = cipher.doFinal(txt);
System.out.println(encText.toString());
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, rsakey);
String decrypted = new String(cipher.doFinal(encText));
System.out.println(decrypted);
} catch (Exception e) {
System.out.println("error" + e);
}
}
精彩评论