一、背景
在使用JDK版本为1.8.0_77时,使用JDK自带的AES加密,能支持128位的key,使用256位的key加密会报错“Illegal key size”
二、环境
JDK 1.8.0_77
三、问题重现
第一步:将JDK环境配置为1.8.0_77
第二步:编写key为128位的异常测试用例,运行查看结果
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.live;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author: sadboy
**/
public class Test {
private static Base64.Encoder base64Encoder = Base64.getEncoder();
public static String encode(String key, String vi, String content) {
try {
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, secretKey, new IvParameterSpec(vi.getBytes()));
byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8);
byte[] byteAES = cipher.doFinal(byteEncode);
return base64Encoder.encodeToString(byteAES);
} catch (Exception var7) {
var7.printStackTrace();
return null;
}
}
public static void main(String[] args) {
//key长度为16位,16*8=128(bit)
String key = "14e83f9f55a142du";
String vi = "ATRe9SJd6lR5JcCb";
String content = "测试内容";
String encode = encode(key, vi, content);
System.out.println(encode);
}
}
运行结果
第三步:编写key为256位的异常测试用例,运行查看结果
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.live;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author: sadboy
**/
public class Test {
private static Base64.Encoder base64Encoder = Base64.getEncoder();
public static String encode(String key, String vi, String content) {
try {
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, secretKey, new IvParameterSpec(vi.getBytes()));
byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8);
byte[] byteAES = cipher.doFinal(byteEncode);
return base64Encoder.encodeToString(byteAES);
} catch (Exception var7) {
var7.printStackTrace();
return null;
}
}
public static void main(String[] args) {
//key长度为32位,32*8=256(bit)
String key = "14e83f9f55a142ducc85fdb27b7b06qg";
String vi = "ATRe9SJd6lR5JcCb";
String content = "测试内容";
String encode = encode(key, vi, content);
System.out.println(encode);
}
}
运行结果
四、解决方案
一、通过反射修改为不校验key的长度
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.live;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author: sadboy
**/
public class Test {
static {
try {
Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
field.setAccessible(true);
field.set(null, java.lang.Boolean.FALSE);
} catch (Exception ex) {
}
}
private static Base64.Encoder base64Encoder = Base64.getEncoder();
public static String encode(String key, String vi, String content) {
try {
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, secretKey, new IvParameterSpec(vi.getBytes()));
byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8);
byte[] byteAES = cipher.doFinal(byteEncode);
return base64Encoder.encodeToString(byteAES);
} catch (Exception var7) {
var7.printStackTrace();
return null;
}
}
public static void main(String[] args) {
//key长度为32位,32*8=256(bit)
String key = "14e83f9f55a142ducc85fdb27b7b06qg";
String vi = "ATRe9SJd6lR5JcCb";
String content = "测试内容";
String encode = encode(key, vi, content);
System.out.println(encode);
}
}
运行结果
二、使用第三方库计算AES
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.live;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* @author: sadboy
**/
public class Test {
private static Base64.Encoder base64Encoder = Base64.getEncoder();
public static String encode(String key, String iv, String content) {
byte[] keyB = key.getBytes();
byte[] ivB = iv.getBytes();
byte[] contentB = content.getBytes(StandardCharsets.UTF_8);
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(keyB), ivB);
aes.init(true, ivAndKey);
byte[] bytes = new byte[0];
try {
bytes = cipherData(aes, contentB);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return base64Encoder.encodeToString(bytes);
}
public static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data) throws Exception {
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
public static void main(String[] args) {
//key长度为32位,32*8=256(bit)
String key = "14e83f9f55a142ducc85fdb27b7b06qg";
String vi = "ATRe9SJd6lR5JcCb";
String content = "测试内容";
String encode = encode(key, vi, content);
System.out.println(encode);
}
}
运行结果
三、截取key的长度,取前面32位作为key
四、引入jar包解决问题
下载地址(JDK8为例)
解决方法
把里面的两个jar包:local_policy.jar 和 US_export_policy.jar 替换掉原来 Jdk 安装目录 $\Java\jre{6 | 7 | 8}\lib\security 下的两个jar包即可 |
五、升级JDK解决问题
JDK 版本在 1.8.0_161之后就没有这个问题了,默认是支持,升级到1.8.0_161及更高版本
实测:JDK版本1.8.0_261没有问题