I would like to encrypt and decrypt a password using 128 bit AES encryption with 16 byte key. I am getting javax.crypto.BadPaddingException
error while decrypting the value. Am I missing anything while decrypting?
public static void main(String args[]) { Test t = new Test(); String encrypt = new String(t.encrypt("mypassword")); System.out.println("decrypted value:" + t.decrypt("ThisIsASecretKey", encrypt)); } public String encrypt(String value) { try { byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); byte[] encrypted = cipher.doFinal(value.getBytes()); System.out.println("encrypted string:" + (new String(encrypted))); return new String(skeySpec.getEncoded()); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalBlockSizeException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (BadPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidKeyException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } return null; } public String decrypt(String key, String encrypted) { try { SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(skeySpec.getEncoded(), "AES")); //getting error here byte[] original = cipher.doFinal(encrypted.getBytes()); return new String(original); } catch (IllegalBlockSizeException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (BadPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidKeyException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } return null; }
Error message
encrypted string:�Bj�.�Ntk�F�`� encrypted key:ThisIsASecretKey decrypted value:null May 25, 2012 12:54:02 PM bean.Test decrypt SEVERE: null 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*..) at bean.Test.decrypt(Test.java:55) at bean.Test.main(Test.java:24)
Finally I am using following Solution based on @QuantumMechanic answer
public class Test { public String encryptionKey; public static void main(String args[]) { Test t = new Test(); String encrypt = t.encrypt("mypassword"); System.out.println("decrypted value:" + t.decrypt(t.encryptionKey, encrypt)); } public String encrypt(String value) { try { // Get the KeyGenerator KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(256); // Generate the secret key specs. SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); String key = new Base64().encodeAsString(raw); this.encryptionKey = key; System.out.println("------------------Key------------------"); System.out.println(key); System.out.println("--------------End of Key---------------"); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); String encrypt = (new Base64()).encodeAsString(cipher.doFinal(value.getBytes())); System.out.println("encrypted string:" + encrypt); return encrypt; } catch (NoSuchAlgorithmException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalBlockSizeException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (BadPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidKeyException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } return null; } public String decrypt(String key, String encrypted) { try { Key k = new SecretKeySpec(Base64.getDecoder().decode(key), "AES"); Cipher c = Cipher.getInstance("AES"); c.init(Cipher.DECRYPT_MODE, k); byte[] decodedValue = Base64.getDecoder().decode(encrypted); byte[] decValue = c.doFinal(decodedValue); String decryptedValue = new String(decValue); return decryptedValue; } catch (IllegalBlockSizeException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (BadPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidKeyException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } return null; }
}
If for a block cipher you're not going to use a Cipher
transformation that includes a padding scheme, you need to have the number of bytes in the plaintext be an integral multiple of the block size of the cipher.
So either pad out your plaintext to a multiple of 16 bytes (which is the AES block size), or specify a padding scheme when you create your Cipher
objects. For example, you could use:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Unless you have a good reason not to, use a padding scheme that's already part of the JCE implementation. They've thought out a number of subtleties and corner cases you'll have to realize and deal with on your own otherwise.
Ok, your second problem is that you are using String
to hold the ciphertext.
In general,
String s = new String(someBytes); byte[] retrievedBytes = s.getBytes();
will not have someBytes
and retrievedBytes
being identical.
If you want/have to hold the ciphertext in a String
, base64-encode the ciphertext bytes first and construct the String
from the base64-encoded bytes. Then when you decrypt you'll getBytes()
to get the base64-encoded bytes out of the String
, then base64-decode them to get the real ciphertext, then decrypt that.
The reason for this problem is that most (all?) character encodings are not capable of mapping arbitrary bytes to valid characters. So when you create your String
from the ciphertext, the String
constructor (which applies a character encoding to turn the bytes into characters) essentially has to throw away some of the bytes because it can make no sense of them. Thus, when you get bytes out of the string, they are not the same bytes you put into the string.
In Java (and in modern programming in general), you cannot assume that one character = one byte, unless you know absolutely you're dealing with ASCII. This is why you need to use base64 (or something like it) if you want to build strings from arbitrary bytes.
import javax.crypto.*; import java.security.*; public class Java { private static SecretKey key = null; private static Cipher cipher = null; public static void main(String[] args) throws Exception { Security.addProvider(new com.sun.crypto.provider.SunJCE()); KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede"); keyGenerator.init(168); SecretKey secretKey = keyGenerator.generateKey(); cipher = Cipher.getInstance("DESede"); String clearText = "I am an Employee"; byte[] clearTextBytes = clearText.getBytes("UTF8"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] cipherBytes = cipher.doFinal(clearTextBytes); String cipherText = new String(cipherBytes, "UTF8"); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedBytes = cipher.doFinal(cipherBytes); String decryptedText = new String(decryptedBytes, "UTF8"); System.out.println("Before encryption: " + clearText); System.out.println("After encryption: " + cipherText); System.out.println("After decryption: " + decryptedText); } } // Output /* Before encryption: I am an Employee After encryption: }?ス?スj6?スm?スZyc?ス?ス*?ス?スl#l?スdV After decryption: I am an Employee */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With