一次AES-256加解密

新接口标准:所有敏感信息都必须使用AES的256位密钥加解密,发现测试在加密的时候报java.security.InvalidKeyException: Illegal key size错误。

产生错误原因:为了数据代码在传输过程中的安全,很多时候我们都会将要传输的数据进行加密,然后等对方拿到后再解密使用。我们在使用AES加解密的时候,在遇到128位密钥加解密的时候,没有进行什么特殊处理;然而,在使用256位密钥加解密的时候,如果不进行特殊处理的话,可能会因为jdk版本的问题出现这个异常java.security.InvalidKeyException: Illegal key size。
为什么会产生这样的错误?

我们做Java开发,都会先在电脑上安装JDK(Java Development Kit) 并配置环境变量,JDK中包含有JRE(Java Runtime Environment,即:Java运行环境),JRE中包括Java虚拟机(Java Virtual Machine)、Java核心类库和支持文件,而我们今天要说的主角就在Java的核心类库中。在Java的核心类库中有一个JCE(Java Cryptography Extension),JCE是一组包,它们提供用于加密、密钥生成和协商以及 Message Authentication Code(MAC)算法的框架和实现,所以这个是实现加密解密的重要类库。

在我们安装的JRE目录下有这样一个文件夹:%JAVE_HOME%\jre\lib\security(%JAVE_HOME%是自己电脑的Java路径,),其中包含有两个.jar文件:“local_policy.jar ”和“US_export_policy.jar”,也就是我们平时说的jar包,这两个jar包就是我们JCE中的核心类库了。JRE中自带的“local_policy.jar ”和“US_export_policy.jar”是支持128位密钥的加密算法,而当我们要使用256位密钥算法的时候,已经超出它的范围,无法支持,所以才会报:“java.security.InvalidKeyException: Illegal key size or default parameters”的异常。

那么我们怎么解决呢?

首先进入%JAVE_HOME%/jre/lib/security/ 目录,看下目录里面是有一个 policy 文件夹,还是有local_policy.jar,

第一种情况:如果有policy 文件夹,说明此版本为JVM启用 无限制强度管辖策略 有了一种新的更简单的方法。

请在 当前文件夹中查找文件 java.security。

现在用文本编辑器打开java.security,并找到定义java安全性属性crypto.policy的行,它可以有两个值limited或unlimited - 默认值是limited。

默认情况下,您应该能找到一条注释掉的行:

#crypto.policy=unlimited

可以通过取消注释该行来启用无限制,删除#:

crypto.policy=unlimited

现在重新启动指向JVM的Java应用程序即可。

第二种情况:没有policy 文件夹,而是直接就有local_policy.jar,US_export_policy.jar两个jar包。

去官方下载JCE无限制权限策略文件。

JDK 5 VERSION

JDK 6 VERSIOn

JDK 7 VERSION

JDK 8 VERSION

下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
将两个jar文件放到%JAVE_HOME%\jre\lib\security目录下覆盖原来文件。

下载时若没有Oracle账户,这里提供一个账户网站。 OracleAccount

加密的问题迎刃而解,接下来是解密,关于解密其实是个小问题,关于Base64转换的问题而已
解密报错 Input length must be multiple of 16 when decrypting with padded cipher

于是乎回头看了一眼加密返回的代码似乎不对劲,直接返回byte数组了,只需要将字节数组byte[]转为base64字符串即可。

错误的返回

只是将byte[]数组toString了

return Arrays.toString(new Base64().encode(result));

正确转换

字节数组转为base64字符串

public String byteToBase64(byte[] b){
 
String image=Base64.getEncoder().encodeToString(b);
 
return image;
 
}

base64字符串转为字节数组

public byte[] base64ToByte(String base64str){
return Base64.getDecoder().decode(base64);
}

福利:AES256加解密工具类

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;


/**
 * @ClassName: AesEncryptUtil
 * @Description: AES256加解密
 * @Author: Richard_Chan
 * @Create: 2022-01-06 16:57
 */
public class AesEncryptUtil {
    //使用AES-256-CBC加密模式,key需要为32位,key和iv可以相同!

    /**
     * 加密方法
     * @param data  要加密的数据
     * @param key 加密key
     * @param iv 加密iv
     * @return 加密的结果
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {
        try {
            SecretKeySpec keys = new SecretKeySpec(key.getBytes(), "AES");
            //"算法/模式/补码方式"
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
            // 偏移量
            byte[] ivBytes = iv.getBytes();
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            // 初始化AES密码器为加密器
            cipher.init(Cipher.ENCRYPT_MODE, keys, ivSpec);
            // 进行AES加密
            byte[] result = cipher.doFinal(dataBytes);
            return java.util.Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密方法
     * @param data 要解密的数据
     * @param key  解密key
     * @param iv 解密iv
     * @return 解密的结果
     * @throws Exception
     */
    public static String desEncrypt(String data, String key, String iv) throws Exception {
        try {
            byte[] encrypted1 = (byte[]) new Base64().decode(data);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
            //使用密钥初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);	//执行操作
            String originalString = new String(original);
            return originalString;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 测试
     */
    public static void main(String args[]) throws Exception {
        // 携程与供应商协商授权用的用户名
        String authUser = "test";
        // 对user做md5(32位小写)
        String key = Md5Helper.MD5(authUser);
        // iv向量,取md5码后16位作为iv
        String iv = key.substring(key.length() - 16);
        // 被aes加密后的密文
        String security = "88i6PIuXaQm7QbUBDut1AA==";
        // 待脱敏的信息,以手机号为例说明
        String mobile = "13800000000";
        // 加密
        String enData = encrypt(mobile, key, iv);

        System.out.println(key);
        System.out.println(iv);
        System.out.println(enData);
        // 解密
        System.out.println(desEncrypt(enData, key, iv));
    }
}
WRITTEN BY:    Richard

I'm discombobulated !