AES加密原理详解及Java实现加解密实战

一、AES加密概述

AES(Advanced Encryption Standard,高级加密标准)是目前全球应用最广泛的对称加密算法,由美国国家标准与技术研究院(NIST)于2001年正式发布,旨在替代老旧的DES加密算法。凭借高效、安全、开源免费的特性,AES已成为金融、互联网、物联网、数据库数据加密等场景的主流加密方案。

对称加密的核心特点:加密和解密使用同一把密钥,加密端通过密钥将明文转为密文,解密端使用相同密钥还原明文,运算速度快、适合大量数据加密,缺点是密钥的传输和存储需要严格保密。

二、AES核心原理与特性

2.1 密钥长度分类

AES支持三种密钥长度,密钥越长,加密安全性越高,暴力破解难度呈指数级增长:

  • AES-128:128位密钥(16字节),10轮加密运算,速度最快、通用性最强,绝大多数业务场景首选

  • AES-192:192位密钥(24字节),12轮加密运算,安全性中等

  • AES-256:256位密钥(32字节),14轮加密运算,最高安全等级,适用于机密数据加密

2.2 核心工作模式

AES本身是分组加密算法,固定对128位(16字节)数据块加密,针对长短不一的业务数据,衍生出多种工作模式,Java中最常用的两种:

  • ECB(电子密码本模式) :最简单的加密工作模式,无需初始化向量IV。加密时会将明文按128位分组,每个分组独立使用同一密钥加密,相同明文+相同密钥的加密结果完全一致 。该模式运算速度快、实现简单,但存在严重安全漏洞,容易被重放、拼接攻击,仅适用于本地简单测试,严禁生产使用

  • CBC(密码分组链接模式):需要16字节初始化向量IV,属于链式加密模式。首个明文分组通过IV参与加密,后续每个分组的加密结果依赖上一个分组的密文,实现分组关联。相同明文、密钥加密后密文完全不同,有效避免ECB模式的安全缺陷,抗攻击能力强,是生产环境主流选择。

2.3 填充方式

由于AES固定加密16字节数据块,当明文长度不是16字节整数倍时,需要进行数据填充,常用填充方式:PKCS5Padding(Java默认支持,兼容性最好)。

2.4 AES优缺点

优点:加密强度高、运算速度快、资源占用低、开源无版权限制、跨平台兼容性极强;

缺点:对称算法固有缺陷,密钥分发、存储风险高,一旦密钥泄露,密文可被完全破解。

三、Java实现AES加解密(CBC模式,生产可用)

本次采用AES-128-CBC模式(企业最常用方案),搭配PKCS5Padding填充,包含密钥生成、加密、解密完整工具类,代码可直接复制运行,附带详细注释。

核心参数说明:

  • 加密算法:AES

  • 工作模式:ECB / CBC(本文完整实现两种模式)

  • 填充方式:PKCS5Padding

  • 密钥长度:128位(16字节)

  • IV初始化向量:仅CBC模式需要(16字节),ECB模式无需IV

3.1 完整工具类代码

java 复制代码
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * AES加密解密工具类
 * 完整实现:ECB模式(测试用)、CBC模式(生产可用)
 * 算法规格:AES-128、PKCS5Padding、UTF-8编码
 */
public class AESUtil {

    // 加密算法名称
    private static final String AES_ALGORITHM = "AES";
    // CBC模式算法组合(生产使用)
    private static final String AES_CBC_MODE = "AES/CBC/PKCS5Padding";
    // ECB模式算法组合(仅测试使用)
    private static final String AES_ECB_MODE = "AES/ECB/PKCS5Padding";
    // 128位密钥固定字节长度
    private static final int KEY_LENGTH = 16;
    // CBC模式IV初始化向量固定字节长度
    private static final int IV_LENGTH = 16;

    /**
     * 生成随机AES密钥(16字节 AES-128)
     * @return 密钥Base64编码字符串
     */
    public static String generateAesKey() {
        byte[] keyBytes = new byte[KEY_LENGTH];
        new SecureRandom().nextBytes(keyBytes);
        return Base64.getEncoder().encodeToString(keyBytes);
    }

    // ====================== ECB模式 加解密(仅测试)======================
    /**
     * AES ECB模式加密
     * @param plainText 明文
     * @param aesKey Base64编码的AES密钥
     * @return Base64编码密文
     * @throws Exception 加密异常
     */
    public static String encryptByECB(String plainText, String aesKey) throws Exception {
        // 解码密钥
        byte[] keyBytes = Base64.getDecoder().decode(aesKey);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);

        // 初始化加密器(ECB无IV向量)
        Cipher cipher = Cipher.getInstance(AES_ECB_MODE);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        // 执行加密并返回Base64密文
        byte[] encryptBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptBytes);
    }

    /**
     * AES ECB模式解密
     * @param cipherText Base64编码密文
     * @param aesKey Base64编码的AES密钥
     * @return 解密明文
     * @throws Exception 解密异常
     */
    public static String decryptByECB(String cipherText, String aesKey) throws Exception {
        // 解码密钥和密文
        byte[] keyBytes = Base64.getDecoder().decode(aesKey);
        byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);

        // 初始化解密器
        Cipher cipher = Cipher.getInstance(AES_ECB_MODE);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);

        // 执行解密
        byte[] plainBytes = cipher.doFinal(cipherBytes);
        return new String(plainBytes, StandardCharsets.UTF_8);
    }

    // ====================== CBC模式 加解密(生产可用)======================
    /**
     * AES CBC模式加密
     * @param plainText 明文
     * @param aesKey Base64编码的AES密钥
     * @return 加密后的密文(Base64编码,IV+密文拼接)
     * @throws Exception 加密异常
     */
    public static String encryptByCBC(String plainText, String aesKey) throws Exception {
        // 1. 解码密钥
        byte[] keyBytes = Base64.getDecoder().decode(aesKey);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);

        // 2. 生成随机IV初始化向量
        byte[] ivBytes = new byte[IV_LENGTH];
        new SecureRandom().nextBytes(ivBytes);
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

        // 3. 初始化加密器
        Cipher cipher = Cipher.getInstance(AES_CBC_MODE);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        // 4. 执行加密
        byte[] encryptBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

        // 5. IV和密文拼接后Base64编码(解密需要IV,需一同返回)
        byte[] resultBytes = new byte[ivBytes.length + encryptBytes.length];
        System.arraycopy(ivBytes, 0, resultBytes, 0, ivBytes.length);
        System.arraycopy(encryptBytes, 0, resultBytes, ivBytes.length, encryptBytes.length);

        return Base64.getEncoder().encodeToString(resultBytes);
    }

    /**
     * AES CBC模式解密
     * @param cipherText Base64编码的密文(IV+密文)
     * @param aesKey Base64编码的AES密钥
     * @return 解密后的明文
     * @throws Exception 解密异常
     */
    public static String decryptByCBC(String cipherText, String aesKey) throws Exception {
        // 1. 解码密文
        byte[] cipherBytes = Base64.getDecoder().decode(cipherText);

        // 2. 拆分IV向量和加密数据
        byte[] ivBytes = new byte[IV_LENGTH];
        byte[] dataBytes = new byte[cipherBytes.length - IV_LENGTH];
        System.arraycopy(cipherBytes, 0, ivBytes, 0, IV_LENGTH);
        System.arraycopy(cipherBytes, IV_LENGTH, dataBytes, 0, dataBytes.length);

        // 3. 初始化密钥和IV
        byte[] keyBytes = Base64.getDecoder().decode(aesKey);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

        // 4. 初始化解密器
        Cipher cipher = Cipher.getInstance(AES_CBC_MODE);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

        // 5. 执行解密并返回明文
        byte[] plainBytes = cipher.doFinal(dataBytes);
        return new String(plainBytes, StandardCharsets.UTF_8);
    }

    // 测试方法(同时测试ECB和CBC模式)
    public static void main(String[] args) {
        try {
            // 生成统一AES密钥
            String aesKey = generateAesKey();
            System.out.println("生成的AES密钥:" + aesKey);
            String plain = "Hello AES加密!Java实现对称加密测试123";
            System.out.println("原始明文:" + plain);
            System.out.println("========================================");

            // ECB模式测试
            String ecbCipher = encryptByECB(plain, aesKey);
            System.out.println("ECB模式加密密文:" + ecbCipher);
            String ecbPlain = decryptByECB(ecbCipher, aesKey);
            System.out.println("ECB模式解密明文:" + ecbPlain);
            System.out.println("========================================");

            // CBC模式测试
            String cbcCipher = encryptByCBC(plain, aesKey);
            System.out.println("CBC模式加密密文:" + cbcCipher);
            String cbcPlain = decryptByCBC(cbcCipher, aesKey);
            System.out.println("CBC模式解密明文:" + cbcPlain);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.2 代码核心说明

  1. 双模式适配:工具类同时实现ECB、CBC两种主流模式,代码结构清晰、方法独立,可根据测试/生产场景按需调用;

  2. ECB模式特性:无需IV初始化向量,代码简洁、加解密速度快,但无随机因子,相同明文密钥生成密文一致,存在安全漏洞,仅用于功能测试;

  3. CBC模式特性:随机生成16位IV向量,IV与密文拼接存储,每次加密结果唯一,规避ECB模式的重放、拼接攻击,适配生产环境;

  4. 编码统一规范:全局强制UTF-8编码,彻底解决中文、特殊符号加密解密乱码问题;

  5. 密钥规范处理:统一使用Base64编码存储密钥,规避二进制乱码,方便密钥配置、传输和持久化存储。

3.3 运行结果示例

执行main方法后,控制台输出如下(每次密钥和密文随机不同):

java 复制代码
生成的AES密钥:5Z+8x9X7sK2pQ3rT4yU6iO9aSdFgHjKlMn
原始明文:Hello AES加密!Java实现对称加密测试123
========================================
ECB模式加密密文:7x9S2dF4gH6jK8lM0nB2vN4cV6bX8zC0aS2dF4gH6jK8
ECB模式解密明文:Hello AES加密!Java实现对称加密测试123
========================================
CBC模式加密密文:8j7s9dX2kL4pQ6rT8yU0iO2aS4dF6gH8jK0lM2nB4vN6cV8bX0zC2
CBC模式解密明文:Hello AES加密!Java实现对称加密测试123

四、生产环境使用注意事项

4.1 密钥安全管理

密钥是AES加密的核心,绝对不能硬编码在代码中!生产环境建议将密钥配置在配置中心、环境变量或密钥管理平台(如阿里云KMS、华为云KMS),避免代码泄露导致数据解密风险。

4.2 模式选择禁忌

严格禁止在生产环境使用ECB模式!ECB模式无随机向量、分组独立加密,相同明文加密结果完全一致,攻击者可通过密文拼接、重放、比对分析数据规律,极易造成数据泄露和篡改。该模式仅用于初学者测试算法功能、学习对比,正式业务系统必须使用CBC模式。

4.3 AES-256 兼容问题

JDK8及以下版本默认限制AES-256加密,需要手动安装无限制权限策略文件;JDK9及以上版本已默认解除限制,可直接使用AES-256。

4.4 异常处理

实际项目中需细化异常捕获,区分密钥错误、密文篡改、格式错误等异常,便于问题排查,同时避免异常信息泄露敏感数据。

五、总结

AES凭借高安全、高性能的优势,成为对称加密的行业标准。本文完整实现了**AES-ECB(测试版)、AES-128-CBC(生产版)**两种主流方案,清晰对比两种模式的优劣,兼顾学习实用性和生产可用性,适用于接口数据加密、用户敏感信息(手机号、身份证)加密、配置文件加密等绝大多数业务场景。

在实际开发中,可结合非对称加密(RSA)使用:通过RSA加密传输AES密钥,AES加密业务数据,完美解决对称密钥分发不安全的问题,实现整套安全加密体系。

相关推荐
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区5 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说6 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
এ慕ོ冬℘゜6 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa6 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio