首页 Java语言AES加密报错-Illegal key size
文章
取消

Java语言AES加密报错-Illegal key size

一、背景

在使用JDK版本为1.8.0_77时,使用JDK自带的AES加密,能支持128位的key,使用256位的key加密会报错“Illegal key size”

二、环境

JDK 1.8.0_77

Oracle下载列表

JDK_8u77_win下载地址

三、问题重现

第一步:将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);
  }

}

运行结果

img.png

第三步:编写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);
  }

}

运行结果

img.png

四、解决方案

一、通过反射修改为不校验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);
    }

}

运行结果

img.png

二、使用第三方库计算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);
    }

}

运行结果

img.png

三、截取key的长度,取前面32位作为key

四、引入jar包解决问题

下载地址(JDK8为例)

下载地址

解决方法

把里面的两个jar包:local_policy.jar 和 US_export_policy.jar 替换掉原来 Jdk 安装目录 $\Java\jre{678}\lib\security 下的两个jar包即可

五、升级JDK解决问题

JDK 版本在 1.8.0_161之后就没有这个问题了,默认是支持,升级到1.8.0_161及更高版本

实测:JDK版本1.8.0_261没有问题

本文由作者按照 CC BY 4.0 进行授权