当前位置:

首页 > 编程开发 > 怎么使用Java实现AES加解密

怎么使用Java实现AES加解密

1.背景知识在密码学中,加密算法分为单向加密和双向加密。对称加密是指加密和解密使用相同的密钥,包括AES加密、DES加密等。非对称加密是指加密和解密使用不同的密钥,包括RSA加密等。单向加密包括MD5、SHA等摘要算法,它们是不可逆的。双向加密包括对称加密和非对称加密。双向加密是可逆的,存在密文的密钥。2.AES简介AES:高级加密标准(AdvancedEncryptionStandard)是美国联邦政府采用的一种区块加密标准,是目前最流行的一种对称加密算法。是用来替代DES的新一代分组加密算法。AES支

本文目录

    1.背景知识

    怎么使用Java实现AES加解密

    • 在密码学中,加密算法分为单向加密和双向加密。

      • 对称加密是指加密和解密使用相同的密钥,包括AES加密、DES加密等。

      • 非对称加密是指加密和解密使用不同的密钥,包括RSA加密等。

      • 单向加密包括MD5、SHA等摘要算法,它们是不可逆的。

      • 双向加密包括对称加密和非对称加密。双向加密是可逆的,存在密文的密钥。

    2.AES简介

    AES: 高级加密标准(Advanced Encryption Standard)是美国联邦政府采用的一种区块加密标准,是目前最流行的一种对称加密算法。

    • 是用来替代DES的新一代分组加密算法。

    • AES支持三种长度的密钥:128位、192位、256位。

    3.AES的加密过程(AES处理单位:字节)

    AES的加解密过程和DES一样,都是通过分组加密、分组解密。所谓分组加密,就是将待加解密的内容按照128位进行分组,将密钥按照128位、192位、256位进行分组,分别将分组后的明文与相应分组后的密钥进行加解密。

    加密: 明文与密钥分组后,对每组:明文组与密钥组处理 -> 轮密钥加 -> 10轮加密 -> 密文组

    解密: 对每组:密文组 -> 轮密钥加 -> 10轮解密 -> 明文组

    明文分组: 每组长度相等,都是128位(16字节);

    密钥分组: 有128位、192位、256位,推荐加密轮数分别为 10、12、14

    密钥组处理: 以密钥分组每组128位为例(则推荐加密轮数为10,前9次执行操作一样,第十次有所不同) 类似地,128位密钥也是用字节为单位的矩阵表示,通过密钥编排函数,形成具有44个元素的序列W[0],W[1], … ,W[43](每个元素4个字节);其中,W[0],W[1],W[2],W[3]为原始密钥,其余40个元素分为10组,每组4个元素(4*4=16字节),分别用于10轮加密。

    AES加密算法涉及4种操作: 字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。

    下图给出了AES加解密的流程:

    • AddRoundKey (轮密钥加)— 矩阵中的每一个字节都与该次轮密钥(round key)做XOR运算;每个子密钥由密钥生成方案产生。

    • SubBytes(字节替代) — 通过非线性的替换函数,用查找表的方式把每个字节替换成对应的字节。

    • ShiftRows(行移位) — 将矩阵中的每个横列进行循环式移位。

    • MixColumns (列混淆)— 为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每列的四个字节。

    怎么使用Java实现AES加解密

    假如一段明文长度是192bit,如果按每128bit一个明文块来拆分的话,第二个明文块只有64bit,不足128bit。这时候怎么办呢?就需要对明文块进行填充(Padding)。

    填充涉及以下三种填充模式:

    • NoPadding:不做任何填充,但是要求明文必须是16字节的整数倍。

    • PKCS5Padding(默认):如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。

    比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}

    • ISO10126Padding:如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字节,最后一个字符值等于缺少的字符数,其他字符填充随机数。

    比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则可能补全为{1,2,3,4,5,a,b,c,d,e,5,c,3,G,$,6}

    4.Java实现

    注意:AES加密包含Base64加密

    加密: AES加密 -> Base64加密 -> 密文

    解密: Base64解密 -> AES解密 -> 明文

    测试地址:点击此处

    4.1 生成密钥和偏移量

    生成16个大小写字母和数字的随机数即可:

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 数字和26个字母组成
    private static final Random RANDOM = new SecureRandom();
    /**
     * 获取长度为 6 的随机字母+数字
     * @return 随机数字
     */
    public static String getRandomNumber() {
        char[] nonceChars = new char[16];  //指定长度为6位/自己可以要求设置
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

    4.2 AESUtil.java 源码

    import org.apache.commons.lang3.RandomStringUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.util.Base64Utils;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    /**
     * AES加密工具类
     *
     * @author ACGkaka
     * @since 2021-06-18 19:11:03
     */
    public class AESUtil {
        /**
         * 日志相关
         */
        private static final Logger LOGGER = LoggerFactory.getLogger(AESUtil.class);
        /**
         * 编码
         */
        private static final String ENCODING = "UTF-8";
        /**
         * 算法定义
         */
        private static final String AES_ALGORITHM = "AES";
        /**
         * 指定填充方式
         */
        private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";
        private static final String CIPHER_CBC_PADDING = "AES/CBC/PKCS5Padding";
        /**
         * 偏移量(CBC中使用,增强加密算法强度)
         */
        private static final String IV_SEED = "1234567812345678";
        /**
         * AES加密
         * @param content 待加密内容
         * @param aesKey  密码
         * @return
         */
        public static String encrypt(String content, String aesKey){
            if(StringUtils.isBlank(content)){
                LOGGER.info("AES encrypt: the content is null!");
                return null;
            }
            //判断秘钥是否为16位
            if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
                try {
                    //对密码进行编码
                    byte[] bytes = aesKey.getBytes(ENCODING);
                    //设置加密算法,生成秘钥
                    SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                    // "算法/模式/补码方式"
                    Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
                    //选择加密
                    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
                    //根据待加密内容生成字节数组
                    byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
                    //返回base64字符串
                    return Base64Utils.encodeToString(encrypted);
                } catch (Exception e) {
                    LOGGER.info("AES encrypt exception:" + e.getMessage());
                    throw new RuntimeException(e);
                }
            }else {
                LOGGER.info("AES encrypt: the aesKey is null or error!");
                return null;
            }
        }
        /**
         * 解密
         * 
         * @param content 待解密内容
         * @param aesKey  密码
         * @return
         */
        public static String decrypt(String content, String aesKey){
            if(StringUtils.isBlank(content)){
                LOGGER.info("AES decrypt: the content is null!");
                return null;
            }
            //判断秘钥是否为16位
            if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
                try {
                    //对密码进行编码
                    byte[] bytes = aesKey.getBytes(ENCODING);
                    //设置解密算法,生成秘钥
                    SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                    // "算法/模式/补码方式"
                    Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
                    //选择解密
                    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
                    //先进行Base64解码
                    byte[] decodeBase64 = Base64Utils.decodeFromString(content);
                    //根据待解密内容进行解密
                    byte[] decrypted = cipher.doFinal(decodeBase64);
                    //将字节数组转成字符串
                    return new String(decrypted, ENCODING);
                } catch (Exception e) {
                    LOGGER.info("AES decrypt exception:" + e.getMessage());
                    throw new RuntimeException(e);
                }
            }else {
                LOGGER.info("AES decrypt: the aesKey is null or error!");
                return null;
            }
        }
        /**
         * AES_CBC加密
         * 
         * @param content 待加密内容
         * @param aesKey  密码
         * @return
         */
        public static String encryptCBC(String content, String aesKey){
            if(StringUtils.isBlank(content)){
                LOGGER.info("AES_CBC encrypt: the content is null!");
                return null;
            }
            //判断秘钥是否为16位
            if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
                try {
                    //对密码进行编码
                    byte[] bytes = aesKey.getBytes(ENCODING);
                    //设置加密算法,生成秘钥
                    SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                    // "算法/模式/补码方式"
                    Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                    //偏移
                    IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));
                    //选择加密
                    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
                    //根据待加密内容生成字节数组
                    byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
                    //返回base64字符串
                    return Base64Utils.encodeToString(encrypted);
                } catch (Exception e) {
                    LOGGER.info("AES_CBC encrypt exception:" + e.getMessage());
                    throw new RuntimeException(e);
                }
            }else {
                LOGGER.info("AES_CBC encrypt: the aesKey is null or error!");
                return null;
            }
        }
        /**
         * AES_CBC解密
         * 
         * @param content 待解密内容
         * @param aesKey  密码
         * @return
         */
        public static String decryptCBC(String content, String aesKey){
            if(StringUtils.isBlank(content)){
                LOGGER.info("AES_CBC decrypt: the content is null!");
                return null;
            }
            //判断秘钥是否为16位
            if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
                try {
                    //对密码进行编码
                    byte[] bytes = aesKey.getBytes(ENCODING);
                    //设置解密算法,生成秘钥
                    SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                    //偏移
                    IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));
                    // "算法/模式/补码方式"
                    Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                    //选择解密
                    cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
                    //先进行Base64解码
                    byte[] decodeBase64 = Base64Utils.decodeFromString(content);
                    //根据待解密内容进行解密
                    byte[] decrypted = cipher.doFinal(decodeBase64);
                    //将字节数组转成字符串
                    return new String(decrypted, ENCODING);
                } catch (Exception e) {
                    LOGGER.info("AES_CBC decrypt exception:" + e.getMessage());
                    throw new RuntimeException(e);
                }
            }else {
                LOGGER.info("AES_CBC decrypt: the aesKey is null or error!");
                return null;
            }
        }
        public static void main(String[] args) {
            // AES支持三种长度的密钥:128位、192位、256位。
            // 代码中这种就是128位的加密密钥,16字节 * 8位/字节 = 128位。
            String random = RandomStringUtils.random(16, "abcdefghijklmnopqrstuvwxyz1234567890");
            System.out.println("随机key:" + random);
            System.out.println();
            System.out.println("---------加密---------");
            String aesResult = encrypt("测试AES加密12", random);
            System.out.println("aes加密结果:" + aesResult);
            System.out.println();
            System.out.println("---------解密---------");
            String decrypt = decrypt(aesResult, random);
            System.out.println("aes解密结果:" + decrypt);
            System.out.println();
            System.out.println("--------AES_CBC加密解密---------");
            String cbcResult = encryptCBC("测试AES加密12456", random);
            System.out.println("aes_cbc加密结果:" + cbcResult);
            System.out.println();
            System.out.println("---------解密CBC---------");
            String cbcDecrypt = decryptCBC(cbcResult, random);
            System.out.println("aes解密结果:" + cbcDecrypt);
            System.out.println();
        }
    }

    4.3 执行结果

    随机key:golrtt58318fx7ol
    ---------加密---------
    aes加密结果:Xy8W9lCeVue9Ao36z+duM7D7WeS5tdBihIMb1q9KpNg=
    ---------解密---------
    aes解密结果:测试AES加密12
    --------AES_CBC加密解密---------
    aes_cbc加密结果:xs3ypQXyd62P9jB0+RvOqxFnHIHBIlVdqoZLuqYNBLw=
    ---------解密CBC---------
    aes解密结果:测试AES加密12456

    4.4 线上验证

    怎么使用Java实现AES加解密

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。