上周帮一个朋友排查线上问题,发现他把用户密码用 MD5「加密」后存数据库,还信誓旦旦地说「这很安全」。我差点一口气没上来——MD5 是哈希,不是加密。哈希不可逆,加密可逆,这是两个完全不同的概念。
做后端开发,对称加密几乎是避不开的必修课。从支付回调验签、接口参数加密传输,到敏感数据落地存储,每天都要跟 AES 打交道。
今天就拿我们站上的 AES 在线加密解密工具,把 AES 实战从头到尾捋一遍。
AES 是什么?为什么是它?
AES(Advanced Encryption Standard)是美国国家标准与技术研究院(NIST)在 2001 年发布的对称加密标准。说人话:加密和解密用同一把钥匙。
| 特性 | AES | 3DES | RC4(已弃用) |
|---|---|---|---|
| 密钥长度 | 128/192/256 位 | 56×3 位 | 40-2048 位 |
| 安全性 | 极高(当前无实用攻击) | 中等(易受中间人攻击) | 低(已知多种攻击) |
| 速度 | 快(硬件加速) | 慢 | 快(但不安全) |
| 行业标准 | ✅ 合规 | ❌ 不推荐 | ❌ 已弃用 |
AES-256 直到今天仍然是学术界公认的安全标准。量子计算机来了?AES-256 在量子攻击下也只需要将密钥长度翻倍,安全性依然够用。
必知参数:5 个关键要素
AES 加解密不是「点一下就行」的事。加密方和解密方必须用完全一样的参数,差一个字母都解不出来。
1. 密钥(Key)
密钥是 AES 的核心。128 位密钥需要 16 个字节(字符),256 位需要 32 个字节。
密钥生成建议:不要用「123456」这种弱口令当密钥。用我们的 密码生成器 生成一个 32 位随机字符串,或者用工具内置的「随机生成密钥」功能。
好密钥示例:a8F#mK2p$9xQ&zN4wE7*LvR@cT1yB6
坏密钥示例:1234567890123456
2. 加密模式(Mode)
| 模式 | 特点 | 适用场景 |
|---|---|---|
| ECB | 每个块独立加密,同样明文→同样密文 | ❌ 不推荐(有模式攻击风险) |
| CBC | 每个块与前一块异或,引入 IV | ✅ 最常用,安全性好 |
| CTR | 将 AES 变成流密码,可并行计算 | 需要并行加解密 |
| GCM | 自带认证标签,检测篡改 | ✅ 推荐用于网络传输 |
| CFB/OFB | 适合流式数据 | 特定协议场景 |
实战中,CBC 和 GCM 是最常见的两个选择。 ECB 除非你非常清楚自己在做什么,否则别用。
3. 初始向量(IV)
CBC、GCM 等模式需要 IV(Initialization Vector)。IV 的作用是让同样明文每次加密都得到不同的密文。
- IV 长度:固定 16 字节(128 位)
- IV 要求:随机生成,可以不保密(通常和密文一起传输)
- IV≠密钥:IV 不需要保密,但每次加密必须用不同的 IV
4. 填充方式(Padding)
AES 加密是按块(16 字节)处理的,最后一块不够 16 字节时要填充。
| 填充方式 | 说明 | 推荐 |
|---|---|---|
| PKCS7 | 缺少 N 字节就补 N 个值为 N 的字节 | ✅ 最常用 |
| ZeroPadding | 补零 | 不推荐(二义性) |
| ISO10126 | 随机填充 | 较少用 |
| NoPadding | 不填充(数据必须刚好 16 的倍数) | 特殊场景 |
5. 输出格式
AES 加密的结果是二进制数据,需要编码后传输。
- Base64:最常用,比原始数据膨胀约 33%,可读性好
- Hex:每个字节转两位十六进制,长度翻倍,适合调试用
- Raw:原始的二进制数据,非文本传输场景用
实战:加密一段 JSON 数据
假设你正在开发支付回调接口,需要把订单信息加密后传给第三方。
原始数据:
{
"order_id": "ORD20260530001",
"amount": 299.00,
"currency": "CNY",
"user_id": "u_10086",
"timestamp": 1748534400
}
加密步骤:
- 打开 AES 加密/解密工具
- 选择模式:CBC
- 密钥:
a8F#mK2p$9xQ&zN4wE7*LvR@cT1yB6(32 位 → AES-256) - IV:点「随机生成」按钮
- 填充:PKCS7(默认)
- 输出:Base64
- 把 JSON 贴到明文区 → 点加密
得到的密文类似这样:
7vG3kR2xY9pQmN4wL8zA5bC1dE6fH0iJ2kL4mO6pQ8rS0tU2vW4xY6zA8
解密时,把这段 Base64 密文、同样的密钥、同样的 IV、同样的模式输入解密区,就能还原原始 JSON。
踩坑记录:我吃过这些亏
坑 1:密钥编码不一致
Java 的 SecretKeySpec 用字节数组构造密钥,而 Node.js 的 crypto.createCipheriv 接受字符串。如果双方的字符编码不一致(UTF-8 vs ASCII),密钥就不同了,解密必定失败。
解决方法:约定密钥用 Base64 或 Hex 编码传输,双方解析成同样的字节序列。
坑 2:IV 忘了传
CBC 模式解密时,IV 必须和加密时一样。很多人加密时生成了随机 IV,解密时却忘了保存,结果死活解不出来。
解决方法:IV 和密文拼接在一起传输。常见做法:IV(16 字节)+ 密文,解密时先取前 16 字节做 IV。
坑 3:跨语言加解密
不同语言对 AES 的默认参数不同:
| 语言/平台 | 默认模式 | 默认填充 | 默认密钥编码 |
|---|---|---|---|
| Java (JCE) | ECB | PKCS5Padding | 字节数组 |
| Python (PyCryptodome) | 无默认 | 必须指定 | 字节数组 |
| Node.js (crypto) | CBC | PKCS7 | UTF-8 字符串 |
| OpenSSL | CBC | PKCS7 | ASCII 字符串 |
跨语言对接时,手动指定所有参数,不要依赖默认值。
最佳实践:生产环境怎么用
加密数据传输
客户端 服务端
│ │
│ 1. 生成随机 AES 密钥 │
│ 2. 用 RSA 公钥加密 AES 密钥 │
│ 3. AES 加密业务数据 │
│ 4. 发送 RSA(AES_KEY) + IV │
│ + AES(Data) │
│─────────────────────────────>│
│ │ 5. RSA 私钥解密出 AES 密钥
│ │ 6. AES 解密出业务数据
这种「RSA 加密 AES 密钥 + AES 加密数据」的混合加密方案,兼顾了非对称加密的安全性和对称加密的效率,是 TLS 协议也在用的思路。
加密后数据标记
建议在加密数据前加一个标识前缀,方便识别是否已加密:
ENCRYPTED_PREFIX = "ENC:"
def encrypt(data, key):
encrypted = aes_encrypt(data, key)
return ENCRYPTED_PREFIX + base64_encode(encrypted)
def decrypt(data, key):
if not data.startswith(ENCRYPTED_PREFIX):
return data # 未加密的直接返回
encrypted = base64_decode(data[len(ENCRYPTED_PREFIX):])
return aes_decrypt(encrypted, key)
密钥轮换
密钥不要写死,定期更换。建议:
- 每 90 天更换一次密钥
- 使用密钥管理服务(AWS KMS / HashiCorp Vault)
- 旧密钥保留至少一个轮换周期用于解密历史数据
总结
AES 加密不是什么玄学,记住五个参数(密钥、模式、IV、填充、输出格式)就能上手。实际工作中,最常用的组合是 AES-256-CBC + PKCS7 + Base64 输出。
下次再看到有人用 MD5 当加密用,记得把这篇文章甩给他。
需要 AES 加解密不想敲命令行的,收藏 NavBox 在线 AES 工具,全浏览器端处理,不断网也能用。