I'm trying to encrypt and decrypt a string using AES but getting an error I don't know how to resolve. This is the code:
public class EncryptionTest{ public static void main(String[] args) { String encrypt = new String(encrypt("1234567890123456")); System.out.println("decrypted value:" + (decrypt("ThisIsASecretKey",encrypt))); } public static 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/CBC/PKCS5Padding"); 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 (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String key, String encrypted) { try { SecretKeySpec skeySpec = new SecretKeySpec(Base64.decodeBase64(key), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(skeySpec.getEncoded(),"AES")); (*) byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); original.toString(); } catch (Exception ex) { ex.printStackTrace(); } return null; } }
When I run it the "decription" values is null. It fails before the (***) !!
It gives me an exception:
java.security.InvalidKeyException: Parameters missing at com.sun.crypto.provider.CipherCore.init(CipherCore.java:388) at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:186) at javax.crypto.Cipher.implInit(Cipher.java:787) at javax.crypto.Cipher.chooseProvider(Cipher.java:849) at javax.crypto.Cipher.init(Cipher.java:1213) at javax.crypto.Cipher.init(Cipher.java:1153) at firma.XmlEncryptionTest.decrypt(EncryptionTest.java:63) at firma.XmlEncryptionTest.main(EncryptionTest.java:41)
where the line 63 is the one before (***). I don't know what am I doing wrong and how to solve. I looked around on the internet but without finding out what coul be that missing parameter
The main issue in your code was caused by a failure to specify an IV value. You must specify an IV value when doing CBC-mode encryption and use that same value when performing the CBC-mode decryption.
Another problem is the mix and match of creating strings from byte arrays and base64-encoding. You also return null
from your decrypt method every time. Even if you meant return original.toString();
, that's still wrong (because toString()
doesn't do what you wish it would on a byte array).
Below is an improved version of your code. It's far from optimal, but it compiles and works. You need to improve this to use a random IV. Also, if you plan to derive keys from passwords, don't just get the bytes, use a derivation function such as PBKDF2. You can see an example of using PBKDF2 in the JNCryptor source.
public class EncryptionTest { public static void main(String[] args) { try { String key = "ThisIsASecretKey"; byte[] ciphertext = encrypt(key, "1234567890123456"); System.out.println("decrypted value:" + (decrypt(key, ciphertext))); } catch (GeneralSecurityException e) { e.printStackTrace(); } } public static byte[] encrypt(String key, String value) throws GeneralSecurityException { byte[] raw = key.getBytes(Charset.forName("UTF-8")); if (raw.length != 16) { throw new IllegalArgumentException("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])); return cipher.doFinal(value.getBytes(Charset.forName("UTF-8"))); } public static String decrypt(String key, byte[] encrypted) throws GeneralSecurityException { byte[] raw = key.getBytes(Charset.forName("UTF-8")); if (raw.length != 16) { 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(encrypted); return new String(original, Charset.forName("UTF-8")); } }
If you use a block-chaining mode like CBC, you need to provide an IvParameterSpec to the Cipher as well.
public class EncryptionTest { static byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; static SecureRandom rnd = new SecureRandom(); static IvParameterSpec iv = new IvParameterSpec(rnd.generateSeed(16)); public static void main(String[] args) { String encrypt = encrypt("1234567890123456"); System.out.println("decrypted value:" + (decrypt("ThisIsASecretKey", encrypt))); } public static String encrypt(String value) { try { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec,iv); byte[] encrypted = cipher.doFinal(value.getBytes()); System.out.println("encrypted string:" + Base64.encodeBase64String(encrypted)); return Base64.encodeBase64String(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String key, String encrypted) { try { SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec,iv); byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; }
}
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