JavaAES加解密报错pto.IllegalBlockSizeExce。。。⼀、问题背景及原因分析
需求对保密性要求严格点,就⽤的 AES + 盐值 + 偏移向量去做,前端加密传递参数,Java 解密参数,然后查询数据,得到数据后再将数据加密返给前端,前端最对数据进⾏解密,得到具体数据使⽤。
在此过程中发现偶尔使⽤ Java AES 解密前端传递的参数时会报这个异常,如下:
at pto.provider.CipherCore.doFinal(CipherCore.java:922)
at pto.provider.CipherCore.doFinal(CipherCore.java:833)
at pto.ineDoFinal(AESCipher.java:446)
pto.Cipher.doFinal(Cipher.java:2165)
at com.symmetric.stDecrpyt(TestAES.java:200)
at com.symmetric.aes.TestAES.main(TestAES.java:48)
字⾯理解很容易,就是解密的字符串的数组必须是 16 的倍数。
1、分析出现此异常的情况:
如果不把加密后的数组拼接为字符串,直接返回,然后使⽤这个加密后的数组进⾏解密就没有任何错误;
但是把加密后的数组拼接为字符串,然后解密时在把此字符串转为数组,就会出现此异常
2、具体分析:
发现当把字节数组转为字符串后,在把字符串.getBytes() 获得字节数组,发现两个字节数组前后不⼀样了 —— 这是报错的位置所在。(声明:new String(byte[]) 和 "str".getBytes() 两个⽅法使⽤的编码⼀样,然后换成其他编码也出现这样情况,也就是说不是编码的问题)
3、原因
(1)为什么数组转字符串,字符串然后转数组会出现,前后两个字节数组的值会不同?
因为并不是每个字节数和编码集上的字符都有对应关系,如果⼀个字节数在编码集上没有对应,编码 new String(byte[]) 后往往解出来的会是⼀些乱码⽆意义的符号,例如:��。
但是解码的时候�这个字符也是⼀个字符在编码表中也有固定的字节数⽤来表⽰,所有解码出来的值必定是编码表中对应的值,除⾮你的字节数组中的字节数正好在编码表中有对应的值,否则编码、解码后的字节数组会不⼀样。
误区:误以为所有的字节数组都可以new String(),然后在通过Bytes()还原。
(2)再说这个异常报错:解密的字节数组必须是16的倍数,这得从AES的原理说起,AES是把数据按16字节分组加密的,所有如果数组长度不是16的倍数会报错。
AES原理:AES是对数据按128位,也就是16个字节进⾏分组进⾏加密的,每次对⼀组数据加密需要运⾏多轮,⽽输⼊密钥的长度可以为128、192和256位,也就是16个字节、24个字节和32个字节,如果⽤户输⼊的密钥长度不是这⼏种长度,也会补成这⼏种长度。
⽆论输⼊密钥是多少字节,加密还是以16字节的数据⼀组来进⾏的,密钥长度的不同仅仅影响加密运⾏的轮数。
4、解决的办法:
(1)可以⽤ base64 对产⽣的数组进⾏编码,然后在解码,这样不会像 new String(byte[])、getBytes() 那样造成数组前后不⼀致,⼀开始我看到⼤部分⼈都是⽤ base64,我也只是以为多⼀层编
码看起来安全⼀些⽽已,没想到 base64 对数组的处理是不会造成误差的
(2)就是直接返回数组,然后再⽤数组解密咯
⼆、解决⽅案
⽽我本⾝就是采⽤了base64 编码的,结果还是偶尔出现这个报错,后来发现了规律,就是只有前端加密的字符串包含特殊字符,如 +,传递给后台去解密就⼀定会报这个错。⽽我本⾝就进⾏了 encodeURIComponent() 进⾏传参。
后来了解到原来原因在这⾥:由于前台通过 url 传过来的加密后的数据到后台接受丢失特殊字符(url 对字符串进⾏编码,但是发现+全部都变成了空格),然后断点调试⼀下,确实 + 变成了空格
1、解决⽅式⼀:Get 参数需要对 URL特殊字符进⾏转义
// 对前台的代码进⾏编码
bankCardNumber = place(/\+/g,"%2B");
// 后台再转码回去 - 就是替换
encrypted = placeAll("%2B", "\\+");
(1)知识
1、URL特殊字符需转义
2、空格换成加号(+)
3、正斜杠(/)分隔⽬录和⼦⽬录
4、问号(?)分隔URL和查询
5、百分号(%)制定特殊字符
6、#号指定书签
url编码处理7、&号分隔参数
(2)转义字符的原因:
如果你的表单使⽤get⽅法提交,并且提交的参数中有“&”等特殊符的话,如果不做处理,在service端
就会将&后⾯的作为另外⼀个参数来看待。例如表单的action为list.jsf?act=Go&state=5,则提交时通过Parameter可以分别取得act和state的值。如果你的本意是act='go&state=5'这个字符串,那么为了在服务端拿到act的准确值,你就必须对&进⾏转义。
(3)url 转义字符原理:
将这些特殊的字符转换成ASCII码,格式为:%加字符的ASCII码,即⼀个百分号%,后⾯跟对应字符的ASCII(16进制)码值。例如空格的编码值是"%20"。
(4)URL特殊符号及对应的⼗六进制值编码:
1、+ URL 中+号表⽰空格 %2B
2、空格 URL中的空格可以⽤+号或者编码 %20
3、/ 分隔⽬录和⼦⽬录 %2F
4、? 分隔实际的 URL 和参数 %3F
5、% 指定特殊字符 %25
6、# 表⽰书签 %23
7、& URL 中指定的参数间的分隔符 %26
8、= URL 中指定参数的值 %3D
2、解决⽅式⼆:使⽤ post 在 body ⾥传参即可(这样就不会存在需 URL 转义的问题)
@PostMapping("/downUrl")
public OperationInfo getDownUrl (@RequestBody String jsonStr) throws Exception{
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
String idStr = String("idStr");
......
}
这⾥就涉及到 Java 对象与 JSON 对象之间的相互转换。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论