java:AES加密和解密

news2024/11/17 21:54:37

java:AES加密和解密

1 前言

对称加密,即单秘钥加密,指加密和解密的过程中,使用相同的秘钥,相比于非对称加密,因仅有一把钥匙,故而速度更快,更适合解密大文件(常见于如视频文件的加密解密中)。AES算法就属于对称加密中的一种。

2 使用

依赖引入:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.6</version>
</dependency>

<!--  spring-boot 3.12.0  -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

AES加密与解密的工具类封装:

package com.xiaoxu.crawler.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * @author xiaoxu
 * @date 2022-11-06 21:24
 * crawlerJ:com.xiaoxu.crawler.utils.AESUtils
 */
public class AESUtils {

    private static final String AES_ALGORITHM = "AES";

    private static final String UTF8 = StandardCharsets.UTF_8.name();

    /* AES加密 String */
    public static String encryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("encode text should not be null or empty.");
        }
        byte[] encodeBytes = encryptByteAES(text.getBytes(StandardCharsets.UTF_8), key);
        return Base64.encodeBase64String(encodeBytes);
    }

    /* AES解密 String*/
    public static String decryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("decode text should not be null or empty.");
        }
        byte[] decodeBytes = decryptByteAES(Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8)), key);
        return new String(decodeBytes, StandardCharsets.UTF_8);
    }

    /* AES加密 originalBytes */
    public static byte[] encryptByteAES(byte[] originalBytes, String key){
        if(ArrayUtils.isEmpty(originalBytes)){
            ExcpUtils.throwExp("encode originalBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", encode key should not be null or empty.");
        }
        Cipher cipher = getAESCipher(key, Cipher.ENCRYPT_MODE);
        byte[] encodeBytes = null;
        try {
            encodeBytes = cipher.doFinal(originalBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": encode byte fail. "+e.getMessage());
        }
        return encodeBytes;
    }

    /* AES解密 encryptedBytes */
    public static byte[] decryptByteAES(byte[] encryptedBytes, String key){
        if(ArrayUtils.isEmpty(encryptedBytes)){
            ExcpUtils.throwExp("decode encryptedBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", decode key should not be null or empty.");
        }
        Cipher cipher = getAESCipher(key, Cipher.DECRYPT_MODE);
        byte[] decodeBytes = null;
        try {
            decodeBytes = cipher.doFinal(encryptedBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": decode byte fail. "+e.getMessage());
        }
        return decodeBytes;
    }

    public static Cipher getAESCipher(String key, int mode){
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", should not be null or empty.");
        }
        Cipher cipher = null;
        SecretKey secretKey;
        try {
            cipher = Cipher.getInstance(AES_ALGORITHM);
            byte[] keyBytes = key.getBytes(UTF8);
            secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            cipher.init(mode, secretKey);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": get cipher instance wrong. "+e.getMessage());
        } catch (UnsupportedEncodingException u){
            ExcpUtils.throwExp(u.getClass().getName()+": key transfer bytes fail. "+u.getMessage());
        } catch (InvalidKeyException i) {
            ExcpUtils.throwExp(i.getClass().getName()+": key is invalid. "+i.getMessage());
        }
        return cipher;
    }

    public static void main(String[] args) {
        String msg = "小徐123testAes!!!";
        String key = "0382f2bcbacfa8be";
//        String key = "0382f2bcbacfa8b";
        System.out.println("AES秘钥长度只能为16、24、32:"+key.getBytes(StandardCharsets.UTF_8).length);
        String s = encryptStrAES(msg, key);
        System.out.println("加密后:"+s);
        String s1 = decryptStrAES(s, key);
        System.out.println("解密后:"+s1);
    }


}

执行如下:

AES秘钥长度只能为162432:16
加密后:ZfZYz9Pk4RcA3QQcv+rKnjM54AQV4s/LjsRiGbkpzw8=
解密后:小徐123testAes!!!

AES的秘钥字节数组长度仅能为16、24、32,修改秘钥长度为15再次执行:

public static void main(String[] args) {
    String msg = "小徐123testAes!!!";
//        String key = "0382f2bcbacfa8be";
    String key = "0382f2bcbacfa8b";
    System.out.println("AES秘钥长度只能为16、24、32:"+key.getBytes(StandardCharsets.UTF_8).length);
    String s = encryptStrAES(msg, key);
    System.out.println("加密后:"+s);
    String s1 = decryptStrAES(s, key);
    System.out.println("解密后:"+s1);
}

结果如下,出现了Invalid AES key length异常:

AES秘钥长度只能为162432:15
Exception in thread "main" com.xiaoxu.crawler.excp.CrawlerForJException: java.security.InvalidKeyException: key is invalid. Invalid AES key length: 15 bytes
	at com.xiaoxu.crawler.utils.ExcpUtils.throwExp(ExcpUtils.java:28)
	at com.xiaoxu.crawler.utils.AESUtils.getAESCipher(AESUtils.java:94)
	at com.xiaoxu.crawler.utils.AESUtils.encryptByteAES(AESUtils.java:50)
	at com.xiaoxu.crawler.utils.AESUtils.encryptStrAES(AESUtils.java:29)
	at com.xiaoxu.crawler.utils.AESUtils.main(AESUtils.java:104)

debug源码可见:

AES使用的秘钥key参数有长度限制,如下可见ElectronicCodeBook的部分源码:

在这里插入图片描述

var2为传入的加密的算法,为AES;var3就是秘钥key转换的字节数组,总共长度为15;调用的是AESCrypt的init方法,如下可见AESCrypt的init源码:

void init(boolean var1, String var2, byte[] var3) throws InvalidKeyException {
    if (!var2.equalsIgnoreCase("AES") && !var2.equalsIgnoreCase("Rijndael")) {
        throw new InvalidKeyException("Wrong algorithm: AES or Rijndael required");
    } else if (!isKeySizeValid(var3.length)) {
        throw new InvalidKeyException("Invalid AES key length: " + var3.length + " bytes");
    } else {
        if (!MessageDigest.isEqual(var3, this.lastKey)) {
            this.makeSessionKey(var3);
            this.lastKey = (byte[])var3.clone();
        }

        this.K = this.sessionK[var1 ? 1 : 0];
    }
}

可见,isKeySizeValid(var3.length)返回false时,即抛出Invalid AES key length异常,如下为isKeySizeValid源码:

AESCrypt部分源码如下:

static final boolean isKeySizeValid(int var0) {
    for(int var1 = 0; var1 < AES_KEYSIZES.length; ++var1) {
        if (var0 == AES_KEYSIZES[var1]) {
            return true;
        }
    }

    return false;
}

其中AES_KEYSIZES为接口中的常量:

interface AESConstants {
    int AES_BLOCK_SIZE = 16;
    int[] AES_KEYSIZES = new int[]{16, 24, 32};
}

即AES的秘钥字节数组长度如果不为16、24、32,将抛出Invalid AES key length异常。

由此可见,AES的秘钥字节数组长度需限制在16、24、32内。

package com.xiaoxu.crawler.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author xiaoxu
 * @date 2022-11-06 21:24
 * crawlerJ:com.xiaoxu.crawler.utils.AESUtils
 */
public class AESUtils {

    private static final String AES_ALGORITHM = "AES";

    private static final String UTF8 = StandardCharsets.UTF_8.name();

    private static final List<Integer> AES_KEYSIZES = Arrays.asList(16, 24, 32);

    public static String newKey(String key){
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("new key should not be empty");
        }
        boolean fl = false;
        int len = key.getBytes(StandardCharsets.UTF_8).length;
        for (int keySize : AES_KEYSIZES) {
            if(len == keySize){
                fl = true;
                break;
            }
        }
        String newKey = key;
        if(!fl){
            /* 32、24、16倒序排序 */
            List<Integer> new_sizes = AES_KEYSIZES.stream().sorted(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2-o1;
                }
            }).collect(Collectors.toList());
            int length = 0;
            for (int var0 = 0; var0 < new_sizes.size(); var0++) {
                if(len > 0 && len < new_sizes.get(new_sizes.size()-1) && var0 == new_sizes.size()-1){
                    length = len;
                }else if(new_sizes.get(var0) <= len){
                    length = new_sizes.get(var0);
                }
                if(new_sizes.get(var0) <= len || (len > 0 && len < new_sizes.get(new_sizes.size()-1)&& var0 == new_sizes.size()-1 ) ){
                    byte[] nKey = new byte[new_sizes.get(var0)];
                    for (int f = 0; f < new_sizes.get(var0); f++) {
	                    /* 不足16位的末尾补0 */
                        nKey[f] = (byte)48;
                    }
                    System.arraycopy(key.getBytes(StandardCharsets.UTF_8),0,nKey,0,length);
                    newKey = new String(nKey, StandardCharsets.UTF_8);
                    break;
                }
            }
        }
        return newKey;
    }

    /* AES加密 String */
    public static String encryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("encode text should not be null or empty.");
        }
        byte[] encodeBytes = encryptByteAES(text.getBytes(StandardCharsets.UTF_8), key);
        return Base64.encodeBase64String(encodeBytes);
    }

    /* AES解密 String*/
    public static String decryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("decode text should not be null or empty.");
        }
        byte[] decodeBytes = decryptByteAES(Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8)), key);
        return new String(decodeBytes, StandardCharsets.UTF_8);
    }

    /* AES加密 originalBytes */
    public static byte[] encryptByteAES(byte[] originalBytes, String key){
        if(ArrayUtils.isEmpty(originalBytes)){
            ExcpUtils.throwExp("encode originalBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", encode key should not be null or empty.");
        }

        Cipher cipher = getAESCipher(key, Cipher.ENCRYPT_MODE);
        byte[] encodeBytes = null;
        try {
            encodeBytes = cipher.doFinal(originalBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": encode byte fail. "+e.getMessage());
        }
        return encodeBytes;
    }

    /* AES解密 encryptedBytes */
    public static byte[] decryptByteAES(byte[] encryptedBytes, String key){
        if(ArrayUtils.isEmpty(encryptedBytes)){
            ExcpUtils.throwExp("decode encryptedBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", decode key should not be null or empty.");
        }
        Cipher cipher = getAESCipher(key, Cipher.DECRYPT_MODE);
        byte[] decodeBytes = null;
        try {
            decodeBytes = cipher.doFinal(encryptedBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": decode byte fail. "+e.getMessage());
        }
        return decodeBytes;
    }

    public static Cipher getAESCipher(String key, int mode){
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", should not be null or empty.");
        }
        Cipher cipher = null;
        SecretKey secretKey;
        try {
            cipher = Cipher.getInstance(AES_ALGORITHM);
            byte[] keyBytes = key.getBytes(UTF8);
            secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            cipher.init(mode, secretKey);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": get cipher instance wrong. "+e.getMessage());
        } catch (UnsupportedEncodingException u){
            ExcpUtils.throwExp(u.getClass().getName()+": key transfer bytes fail. "+u.getMessage());
        } catch (InvalidKeyException i) {
            ExcpUtils.throwExp(i.getClass().getName()+": key is invalid. "+i.getMessage());
        }
        return cipher;
    }

    public static void main(String[] args) {
        String msg = "小徐123testAes!!!";
//        String key = "0382f2bcbacfa8be";
        String key = "0382f2bcbacfa8b";
        System.out.println("转换前的秘钥长度:" + key.getBytes(StandardCharsets.UTF_8).length);
        System.out.println("转换后秘钥:"+newKey(key)+";长度:"+newKey(key).getBytes(StandardCharsets.UTF_8).length);

        System.out.println("AES秘钥长度只能为16、24、32:"+newKey(key).getBytes(StandardCharsets.UTF_8).length);
        String s = encryptStrAES(msg, newKey(key));
        System.out.println("加密后:"+s);
        String s1 = decryptStrAES(s, newKey(key));
        System.out.println("解密后:"+s1);
    }
}

可如上简单对秘钥长度作出填充,执行结果如下:

转换前的秘钥长度:15
转换后秘钥:0382f2bcbacfa8b0;长度:16
AES秘钥长度只能为162432:16
加密后:AAz4gPXlduN+l1OX0BV9nWXqJqhXRS3ThRQXJsU0lWM=
解密后:小徐123testAes!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/97782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ESP32的arduino IDE代码使用flash download tool进行烧录

ESP32的arduino IDE代码使用flash download tool进行烧录前言arduino代码烧录arduino下载了一些什么文件flash download tool工具烧录总结前言 最近遇到用户在使用 arduino IDE开发环境编写了ESP32的代码&#xff0c;希望提供编写好的程序给用户烧录&#xff0c;但是又不希望让…

Kotlin + SpringBoot + JPA 服务端开发

Kotlin SpringBoot JPA 服务端开发 本篇主要介绍一下 kotlin springboot的服务端开发环境搭建 1.概述 Kotlin 是一个基于JVM的编程语言, 是IDEA开发工具 jetbrains 公司开发的语言,也被google选为android开发的首选语言, 因为它是完全兼容Java的 所以也可以做后端开发 比如…

[Java] 如何理解和设置ThreadPoolExecutor三大核心属性?什么情况下工作线程数会突破核心线程数?任务拒绝策略都有哪些?

文章目录前言ThreadPoolExecutor类是什么&#xff1f;ThreadPoolExecutor的三大核心属性1. 核心线程数&#xff08;corePoolSize&#xff09;属性2. 任务队列&#xff08;workQueue&#xff09;属性3. 最大线程数&#xff08;maximumPoolSize&#xff09;属性总结&#xff1a;T…

【开发百宝箱之猿如意使用指南】「工欲成其事,必先利其器」一文教你如何通过“猿如意”便捷的使用数据库管理工具DBeaver

开发百宝箱之猿如意使用指南欢迎您使用“猿如意”百宝箱大家科普一下什么是猿如意&#xff1f;赶快趁热下载个【猿如意】吧每个程序猿值得拥有的学习开发工作必备“良药”没有猿如意的“我们”&#xff08;猿如意帮我们解决了哪些问题&#xff1f;&#xff09;【如何快速下载自…

非零基础自学Golang 第12章 接口与类型 12.2 接口的创建与实现 12.2.1 接口创建

非零基础自学Golang 文章目录非零基础自学Golang第12章 接口与类型12.2 接口的创建与实现12.2.1 接口创建第12章 接口与类型 12.2 接口的创建与实现 Go语言接口是方法的集合&#xff0c;使用接口是实现模块化的重要方式。 下面将重点介绍如何创建和实现一个Go语言接口。 12…

Pytest用例执行的先后顺序

[内部资源] 想拿年薪30W的软件测试人员&#xff0c;这份资料必须领取~ 温馨提示 「本篇约1600字&#xff0c;看完需3-5分钟&#xff0c;学习学半小时&#xff0c;加油&#xff01;」 先看普通函数运行顺序 import pytestdef test_one():print("我是清安")def tes…

React学习27(react-redux多组件共享数据)

项目结构 准备工作 1&#xff09;定义一个person组件&#xff0c;和count组件通过redux共享数据 2&#xff09;为person组件编写&#xff1a;reducer &#xff0c;action和contant常量 3&#xff09;重点&#xff1a;Person的reducer和Count的reducer要用combineReducers进行…

深度学习-优化器

优化器 文章目录优化器1. 引言1. SGD1.1 vanilla SGD1.2 SGD with Momentum1.3 SGD with Nesterov Acceleration2. AdaGrad3. RMSProp4. AdaDelta5. Adam优化器选择出处1. 引言 优化算法可以分成一阶优化和二阶优化算法&#xff0c;其中一阶优化就是指的梯度算法及其变种&#…

Linux——vi,vim的使用

三种模式 正常模式 以vi或vim打开一个档案就直接进入一般模式了(这是默认的模式)。在这个模式中&#xff0c;你可以使用 【上下左右】 按键来移动光标&#xff0c;你可以使用 【删除字符】或 【删除整行】来处理档案内容&#xff0c;也可以使用 【复制&#xff0c;粘贴】来处…

3D激光里程计其三:点云畸变补偿

3D激光里程计其三&#xff1a;点云畸变补偿1. 产生原因2. 补偿方法Reference: 深蓝学院-多传感器融合 1. 产生原因 一帧点云&#xff1a;通常指雷达内部旋转一周扫描得到的点的集合。 优点&#xff1a;有足够数量的点云才能进行匹配&#xff0c;且一周正好是周围环境的完整采…

认识Java中的异常处理

文章目录Java异常处理异常体系的介绍常见运行时异常常见编译时异常Java异常处理 异常体系的介绍 什么事异常处理? 异常是程序在“编译”或者“执行”的过程中可能出现的问题&#xff0c;比如: 数组索引越界、空指针异常、 日期格式化异常&#xff0c;等… 注意&#xff1a;语…

常用损失函数

常见损失函数 文章目录常见损失函数引言回归1. 均方差2.平均绝对误差(MAE)3. 均方根误差(RMSE)4. 交叉熵分类二分类多分类引言 无论在机器学习还是深度领域中,损失函数都是一个非常重要的知识点。损失函数&#xff08;Loss Function&#xff09;是用来估量模型的预测值 f(x) 与…

Eslint 8.23 Flat Config 新配置迁移升级指南

前言 直入正题&#xff0c;eslint 目前为止的配置文件格式&#xff08; 如 .eslintrc.js &#xff09; 存在很多无法避免的历史问题&#xff1a; 配置格式混乱&#xff0c;层层不明确的继承&#xff0c;不易理解。 插件配置时不支持实体运用&#xff08;不能传 function / ob…

学完框架后的反思—为什么要使用框架?

学习前端也有一定的时间了,最近在网上看到了一个问题让我反思了很久——为什么要使用前端框架? 我发现自己当初学习框架时并没有想那么多,只是看中了其在业界企业的应用,大部分公司开发页面基本上都是使用框架进行开发的,而最为被大厂广泛接受的就是 React 框架,所以我当…

二棕榈酰磷酯酰乙醇胺-聚乙二醇-叠氮 DPPE-PEG-N3简介,可用于药物传递、基因转染和生物分子修饰。

二棕榈酰磷酯酰乙醇胺-聚乙二醇-叠氮 DPPE-PEG-N3 中文名称&#xff1a;二棕榈酰磷酯酰乙醇胺-聚乙二醇-叠氮基 英文名称&#xff1a;DPPE-PEG-N3 英文别名&#xff1a; 1,2-dipalmitoyl-sn-glycero-3-phosphoethanolamine-PEG-Azide 分子量&#xff08;PEG&#xff09;&a…

Linux——任务调度

at定时任务 基本介绍 at命令是一次性定时计划任务&#xff0c;at地守护线程atd会以后台模式运行&#xff0c;检查作业队列来运行默认情况下&#xff0c;atd守护进程没60秒检查作业队列&#xff0c;有作业时&#xff0c;会检查作业运行时间&#xff0c;如果时间于当前时间匹配…

Dubbo 1 分布式系统中的相关概念 1.2 集群和 分布式

Dubbo 【黑马程序员Dubbo快速入门&#xff0c;Java分布式框架dubbo教程】 1 分布式系统中的相关概念 文章目录Dubbo1 分布式系统中的相关概念1.2 集群和 分布式1.2.1 集群和分布式1.2.2 集群和分布式 【互联网 中】1.2 集群和 分布式 1.2.1 集群和分布式 集群&#xff1a;很…

使用synchornized和ReentrantLock来解决并发错误

文章目录什么是并发错误&#xff1f;并发错误是如何产生的&#xff1f;演示并发错误如何解决并发错误使用synchornized解决并发错误使用ReentrantLock解决并发错误什么是并发错误&#xff1f; 多个线程共享操作同一个对象的时候&#xff0c;线程体当中连续的多行操作未必能够连…

下个文档还要马内?还好我会Python,教大家来一手强制复制粘贴

前因后果 公司有人阳了&#xff0c;今天在家上班&#xff0c;突然小姨子就问我有没有baidu文库会员&#xff0c;想下载点东西&#xff0c;我心想这还要会员&#xff1f;用Python不是分分钟的事情&#xff01; 然后我非常自信的告诉她不用会员随便下载&#xff0c;结果她顺势想…

10两级电力市场环境下计及风险的省间交易商最优购电模型

参考文章&#xff1a; 两级电力市场环境下计及风险的省间交易商最优购电模型—郭立邦&#xff08;电网技术2019&#xff09; 主要内容&#xff1a; 为进一步推动电力市场建设&#xff0c;促进电力资源大范围优化配置&#xff0c;我国正逐步建成包含省间与省内电力交易的两级…