Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What causes the error "java.security.InvalidKeyException: Parameters missing"? [duplicate]

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

like image 557
Igr Avatar asked Jun 26 '13 13:06

Igr


2 Answers

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"));   } } 
like image 176
Duncan Jones Avatar answered Sep 19 '22 12:09

Duncan Jones


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; } 

}

like image 31
dijkstra Avatar answered Sep 17 '22 12:09

dijkstra