是什么导致错误“java.security.InvalidKeyException:参数缺失”?
我正在尝试使用AES加密和解密字符串,但收到错误我不知道如何解决。 这是代码:
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; } }
当我运行它时,“decription”值为null。 它在(***)之前失败了!!
它给了我一个例外:
java.security.InvalidKeyException:缺少参数 在com.sun.crypto.provider.CipherCore.init(CipherCore.java:388) 在com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:186) 在javax.crypto.Cipher.implInit(Cipher.java:787) 在javax.crypto.Cipher.chooseProvider(Cipher.java:849) 在javax.crypto.Cipher.init(Cipher.java:1213) 在javax.crypto.Cipher.init(Cipher.java:1153) 在firma.XmlEncryptionTest.decrypt(EncryptionTest.java:63) 在firma.XmlEncryptionTest.main(EncryptionTest.java:41)
其中第63行是前一行(***)。 我不知道我做错了什么以及如何解决。 我在互联网上环顾四周,但没有找到那个缺少参数的东西
代码中的主要问题是由于未能指定IV值。 您必须在执行CBC模式加密时指定IV值,并在执行CBC模式解密时使用相同的值。
另一个问题是从字节数组和base64编码创建字符串的混合和匹配。 您每次都会从解密方法返回null
。 即使你的意思是return original.toString();
,这仍然是错误的(因为toString()
不会在字节数组上做你想做的事情)。
以下是代码的改进版本。 它远非最佳,但它编译和工作。 你需要改进这个以使用随机IV。 此外,如果您计划从密码派生密钥,请不要只获取字节,使用派生函数,如PBKDF2 。 您可以在JNCryptor源中看到使用PBKDF2的示例 。
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")); } }
如果您使用像CBC这样的块链接模式,则还需要为Cipher提供IvParameterSpec。
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; }
}
即使它太晚了,但我仍在为其他人提供我的解决方案。请参考下面的工作代码来加密和解密数据。
public static byte[] encrypt(String value) { byte[] encrypted = null; try { byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; Key skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] iv = new byte[cipher.getBlockSize()]; IvParameterSpec ivParams = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, skeySpec,ivParams); encrypted = cipher.doFinal(value.getBytes()); System.out.println("encrypted string:" + encrypted.length); } catch (Exception ex) { ex.printStackTrace(); } return encrypted; } public static byte[] decrypt(byte[] encrypted) { byte[] original = null; Cipher cipher = null; try { byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; Key key = new SecretKeySpec(raw, "AES"); cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //the block size (in bytes), or 0 if the underlying algorithm is not a block cipher byte[] ivByte = new byte[cipher.getBlockSize()]; //This class specifies an initialization vector (IV). Examples which use //IVs are ciphers in feedback mode, eg, DES in CBC mode and RSA ciphers with OAEP encoding operation. IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte); cipher.init(Cipher.DECRYPT_MODE, key, ivParamsSpec); original= cipher.doFinal(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return original; }