java 使用AES解密报这个异常,字面理解很容易,就是解密的字符串的数组必须是16的倍数
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:922)at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:833)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.symmetric.aes.TestAES.testDecrpyt(TestAES.java:200)
at com.symmetric.aes.TestAES.main(TestAES.java:48)
1.使用的代码:
package com.symmetric.aes; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; public class TestAES2 { private static final String enType_AES = "AES"; private static final String pathStr = "D://aes.key"; private static final String testStr = "AES tips you shoutld try again"; public static void main(String[] args) { SecretKey genSecretKey = testGenerateKey(); String string = genSecretKey.toString(); // byte[] testEncryptBytes = testEncryptBytes(testStr, genSecretKey); // String testDecode = testDecrptBytes(testEncryptBytes, genSecretKey); // System.out.println(testDecode); //直接操作数组,加密解密正常 String testEncrypt = testEncrypt(testStr, genSecretKey); String testDecode2 = testDecrpt(testEncrypt, genSecretKey); System.out.println(testDecode2); } /** * 生成密钥 * 通过传递SecureRandom对象进行初始化 * 不指定种子或密钥 * @return */ public static SecretKey testGenerateKey(){ SecretKey genSecretKey = null; try { KeyGenerator kGenerator = KeyGenerator.getInstance(enType_AES); SecureRandom sRandom = new SecureRandom(); kGenerator.init(sRandom);//不使用种子,每次生成的都不同 genSecretKey = kGenerator.generateKey(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return genSecretKey; } /** * 加密 * @param str * @param genSecretKey * @return */ public static byte[] testEncryptBytes(String str,SecretKey genSecretKey){ byte encrypt [] = null; try { Cipher cipher = Cipher.getInstance(enType_AES); cipher.init(Cipher.ENCRYPT_MODE, genSecretKey);//加密模式,密钥 //cipher.update(str.getBytes()); encrypt = cipher.doFinal(str.getBytes()); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return encrypt; } public static String testEncrypt(String str,SecretKey genSecretKey){ byte[] testEncryptBytes = testEncryptBytes(str, genSecretKey); System.out.println(Arrays.toString(testEncryptBytes)); System.out.println("加密后的数组长度"+testEncryptBytes.length); return (new String(testEncryptBytes)); } /** * 解密 * @param str * @param genSecretKey * @return */ public static String testDecrptBytes(byte[] bytes,SecretKey genSecretKey){ String decoderStr = null; try { Cipher cipher = Cipher.getInstance(enType_AES); cipher.init(Cipher.DECRYPT_MODE, genSecretKey);//解密模式 //cipher.update(str.getBytes()); byte[] doFinal = cipher.doFinal(bytes); decoderStr = new String(doFinal); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return decoderStr; } public static String testDecrpt(String str,SecretKey genSecretKey){ System.out.println(Arrays.toString(str.getBytes())); System.out.println("数组的大小"+str.getBytes().length); return testDecrptBytes(str.getBytes(), genSecretKey); } }
2.分析出现此异常的情况:
如果不把加密后的数组拼接为字符串,直接返回,然后使用这个加密后的数组进行解密没有任何错误
但是把加密后的数组拼接为字符串,然后解密时在把此字符串转为数组,就会出现此异常
3.具体分析:
发现当把字节数组转为字符串后,在把字符串.getBytes()获得数组,发现两个字节数组前后不一样了,,这是报错的位置所在
声明:new String(byte[]),和"str".getBytes(),两个方法使用的编码一样,然后换成其他编码也出现这样情况
3.原因:
3.1. 为什么数组转字符串,字符串然后转数组会出现,前后两个字节数组的值会不同,因为并不是每个字节数和编码集上的字符都有对应关系,如果一个字节数在编码集上没有对应,编码new String(byte[]) 后,往往解出来的会是一些乱码无意义的符号:例如:��,
但是解码的时候,�这个字符也是一个字符在编码表中也有固定的字节数用来表示,所有解码出来的值必定是编码表中对应的值,除非你的字节数组中的字节数正好在编码表中有对应的值,否则编码,解码后的字节数组会不一样
误区: 误以为所有的字节数组都可以new String(),然后在通过String.getBytes()还原
3.2.再说这个异常报:解密的字节数组必须是16的倍数,这得从AES的原理说起,AES是把数据按16字节分组加密的,所有如果数组长度不是16的倍数会报错
AES原理:AES是对数据按128位,也就是16个字节进行分组进行加密的,每次对一组数据加密需要运行多轮。而输入密钥的长度可以为128、192和256位,也就是16个字节、24个字节和32个字节,如果用户输入的密钥长度不是这几种长度,也会补成这几种长度。无论输入密钥是多少字节,加密还是以16字节的数据一组来进行的,密钥长度的不同仅仅影响加密运行的轮数。