I'm trying to get some encryption/decryption going using AES/CBC/PKCS5Padding and am getting a strange result. Depending on the original value I use to encrypt I get an exception:
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
To test this out, I wrote a little function that starts with a string and progressively makes it bigger, trying to encrypt the string and decrypt the encrypted result in each iteration.
Encrypt and decrypt
Encrypt and decrypt
Encrypt and decrypt
Encrypt and decrypt
Encrypt and decrypt
If consistently fails to decrypt the encrypted values in items 0 and 4 (first and last). It successfully decrypts the other values.
Any clues what may be causing this?
Here is the output of the program:
0 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [5]
This is the ciphertext encrypted [ÂZ??¢?»NÔå?Ó^Ç ]
Encrypted Value = [C25A863FA23FBB4ED4E53FD35E7FC7A0]
DECRYPT Key: [00000000000000000000000000000000] value: [C25A863FA23FBB4ED4E53FD35E7FC7A0]
This is the ciphertext [[B@5fdef03a]
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:977)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1058)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:855)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)
at com.mgl.siebel.crypt.AES256Crypt.decrypt(AES256Crypt.java:35)
at com.mgl.siebel.crypt.AES256Crypt.test2(AES256Crypt.java:85)
at com.mgl.siebel.crypt.AES256Crypt.main(AES256Crypt.java:101)
Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
1 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [55]
This is the ciphertext encrypted []*çü×z%?eÑ¥zx~÷]
Encrypted Value = [5DAD2AE7FCD77A259665D1A57A787EF7]
DECRYPT Key: [00000000000000000000000000000000] value: [5DAD2AE7FCD77A259665D1A57A787EF7]
This is the ciphertext [[B@5ccd43c2]
Decrypted Value = [55]
2 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [555]
This is the ciphertext encrypted [M÷o?gI¶àeØÖ8c.+]
Encrypted Value = [4DF76F916749B6E065D807D638632E2B]
DECRYPT Key: [00000000000000000000000000000000] value: [4DF76F916749B6E065D807D638632E2B]
This is the ciphertext [[B@4aa8f0b4]
Decrypted Value = [555]
3 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [5555]
This is the ciphertext encrypted [ÖFè7tÔ·ðGÂ?WÂGs ]
Encrypted Value = [D646E83774D4B7F047C28657C24773A0]
DECRYPT Key: [00000000000000000000000000000000] value: [D646E83774D4B7F047C28657C24773A0]
This is the ciphertext [[B@7960847b]
Decrypted Value = [5555]
4 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [55555]
This is the ciphertext encrypted [ȱiã?'èÀ0<eäy?]
Encrypted Value = [C80EB169E33F27E8C0AD303C65E4791B]
DECRYPT Key: [00000000000000000000000000000000] value: [C80EB169E33F27E8C0AD303C65E4791B]
This is the ciphertext [[B@2aae9190]
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:977)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1058)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:855)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)
at com.mgl.siebel.crypt.AES256Crypt.decrypt(AES256Crypt.java:35)
at com.mgl.siebel.crypt.AES256Crypt.test2(AES256Crypt.java:85)
at com.mgl.siebel.crypt.AES256Crypt.main(AES256Crypt.java:101)
Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
Here is the code being executed
public String decrypt(String key, String encryptedRawValue) throws Exception {
System.out.println("DECRYPT Key: [" + key + "] value: [" + encryptedRawValue + "]");
try {
if ((key == null) || (encryptedRawValue == null)) {
throw new Exception("key and value must not be null");
}
// convert raw value into its original encrypted sequence of bytes
byte[] ciphertext = DatatypeConverter.parseHexBinary(encryptedRawValue);
System.out.println("This is the ciphertext [" + ciphertext + "]");
byte[] raw = key.getBytes(Charset.forName("UTF-8"));
if (raw.length != 32) {
throw new IllegalArgumentException("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] original = cipher.doFinal(ciphertext);
String plainTextValue = new String(original, Charset.forName("UTF-8"));
return (plainTextValue);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public String encrypt(String key, String value) throws Exception {
System.out.println("ENCRYPT Key: [" + key + "] value: [" + value + "]");
try {
byte[] raw = key.getBytes(Charset.forName("UTF-8"));
if (raw.length != 32) {
throw new Exception("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
String encryptedValue = new String(cipher.doFinal(value.getBytes(Charset.forName("UTF-8"))));
System.out.println("This is the ciphertext encrypted [" + encryptedValue + "]");
String rawValue = DatatypeConverter.printHexBinary(encryptedValue.getBytes());
return (rawValue);
} catch(Exception e) {
e.printStackTrace();
throw e;
}
}
private void test2() throws Exception {
String key = "00000000000000000000000000000000";
try {
String value = "";
for (int i=0; i < 5; i++) { // loop 5 times encrypting and decrypting
System.out.println("\n" + i + " **************************************\n");
try {
value = value + "5";
String encryptedValue = this.encrypt(key, value);
System.out.println("Encrypted Value = ["+ encryptedValue + "]");
String plainTextValue = this.decrypt(key, encryptedValue);
System.out.println("Decrypted Value = ["+ plainTextValue + "]");
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
AES256Crypt c = new AES256Crypt();
try {
c.test2();
} catch(Exception e) {
e.printStackTrace();
}
}
}
String encryptedRawValue
This cannot work. Strings are a sequence of characters. Encrypted data is a sequence of bytes. If we live in magic unicornland where unicode, and more generally western characters, can just be waved away as being non-existent, you can write really bad code and conflate the two. This was common in ye olden days. it's so bad, it's a major reason for why python 2 decided to up and ditch it all and move to python 3.
There is only one fix. Stop doing this. The correct type is byte[]
. If you then need this byte[] rendered in string form for some reason, then the only sane reason is because it needs to be rendered in a highly limited venue, such as an email. In which case, you should base64 encode it. Search the web for 'java base64' on how to do this, if you must. Those APIs get it right: The 'encode' method takes a byte[] and returns a String, and the decode method takes a String and returns a byte[].
Fix this issue and the problem goes away.
String encryptedValue = new String(cipher.doFinal(value.getBytes(Charset.forName("UTF-8"))));
Do not wrap the result of doFinal
into new String
. Take the byte array. That's your data.