如何解密 AES 文本
- 默认已选中 解密,并载入了一个示例。它的密码是
correct horse battery staple,你可以先看看成功解密的效果。 - 把你的字符串粘贴到 加密的 Base64 中。标准 Base64 和 URL 安全 Base64 都可以,有没有
=都行,换行会被忽略。 - 密钥类型选择 密码 时,输入密码;如果发送方改过迭代次数,还要填入相同的 PBKDF2 迭代次数(默认 310,000)。选择 原始密钥 时,以 Base64 或 hex 形式粘贴 16、24 或 32 字节的密钥。
- 点击 解密,或在密码框中按回车。消息会显示在 解密后的文本 下方。如果内容不是 UTF-8 文本,则以 Base64 显示,点击 下载 可以保存原始字节。
解密成功时会显示“已解密并通过认证”:GCM 标签匹配,说明文本与发送方加密的内容完全一致。
Base64 解密还是 Base64 解码?
很多人手里只有一个普通的 Base64 字符串,却去搜索“Base64 解密”。Base64 是一种没有密钥的编码,这样的字符串只需要解码,Base64 解码器就能做到。一个简单的判断方法:如果解码后得到可读的文本或可识别的文件,那它从来就没有被加密过。如果得到的是看似随机的字节,而且有人给了你一个密码,那你拿到的就是密文,这个页面正适合你,前提是它是按下面的格式生成的。Base64 不是加密解释了两者的区别。
用已知的值测试
下面这个字符串是在浏览器之外,用格式文档中的 Python encrypt() 函数、以密码 correct horse battery staple 加密得到的:
SD8wB+5V2GRfupy/yEY1xTUE8d5z9fwbzQ0gxeRzbtym1I4O7A9Q2NYnbYxEPYgvwJ4Q7DU5q/sugOx+B1Ro3ujkDXvnl/y2XYuji9A=
它解密后是 Meet me at the usual place at 10.。这 104 个字符解码为 77 个字节:16 字节盐值、12 字节 IV、33 字节密文和 16 字节标签。对于你粘贴的任何字符串,结果下方的明细都会显示同样的拆分。
错误提示及其含义
| 提示 | 常见原因 |
|---|---|
| 解密失败:密码或密钥错误、模式错误,或数据已被修改 | 密码错误、迭代次数不同、把原始密钥用于密码模式的数据(或者反过来),或者复制时丢了字符 |
| 太短,不可能是此工具生成的密文 | 字符串被截断了,或者根本不是本工具生成的 |
| AES 密钥必须是 16、24 或 32 字节 | 原始密钥长度不对;请检查它是 Base64 还是 hex |
| 此浏览器不支持 192 位 AES 密钥 | Chrome 和 Edge 只允许 128 位和 256 位密钥;请改用 Python 或 Node.js 解密 |
| 迭代次数必须在 1,000 到 10,000,000 之间 | 迭代次数字段中的值不合理 |
GCM 不区分密码错误和数据被篡改:两种情况下标签都不匹配,也都不会输出任何内容。CBC 等不带认证的模式则可能返回一堆乱码而不报错。
本工具无法打开的字符串
这里的格式是固定的:密码模式为 Base64( salt[16] ‖ iv[12] ‖ ciphertext ‖ tag[16] ),原始密钥模式为 Base64( iv[12] ‖ ciphertext ‖ tag[16] ),算法为 AES-GCM,密钥派生为 310,000 次迭代的 PBKDF2-HMAC-SHA256。其他工具会写出其他格式,开头几个字符往往就能暴露它们:
| 开头 | 生成方 | 解密方式 |
|---|---|---|
U2FsdGVkX1 | 使用口令的 openssl enc 或 CryptoJS(Salted__ 头部) | 用原来的加密算法和 KDF 选项执行 openssl enc -d -a |
gAAAAA | Fernet 令牌(Python cryptography) | Fernet(key).decrypt(token) |
-----BEGIN PGP MESSAGE----- | OpenPGP | gpg --decrypt |
-----BEGIN AGE ENCRYPTED FILE----- | age | age --decrypt |
更多可识别的开头收录在 常见 Base64 开头中。
在 Node.js 和 Python 中解密
同样的格式不用浏览器也能处理。在 Node.js 中:
const crypto = require("crypto");
function decrypt(b64, password) {
const data = Buffer.from(b64, "base64");
const salt = data.subarray(0, 16), iv = data.subarray(16, 28);
const tag = data.subarray(data.length - 16), ct = data.subarray(28, data.length - 16);
const key = crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256");
const d = crypto.createDecipheriv("aes-256-gcm", key, iv);
d.setAuthTag(tag);
return Buffer.concat([d.update(ct), d.final()]).toString("utf8");
}
function encrypt(text, password) {
const salt = crypto.randomBytes(16), iv = crypto.randomBytes(12);
const key = crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256");
const c = crypto.createCipheriv("aes-256-gcm", key, iv);
const ct = Buffer.concat([c.update(text, "utf8"), c.final()]);
return Buffer.concat([salt, iv, ct, c.getAuthTag()]).toString("base64");
}
对于 Python 中原始密钥模式的字符串,跳过盐值和 PBKDF2,IV 从第 0 字节开始:
import base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
def decrypt_raw(b64, key_b64):
data = base64.b64decode(b64)
iv, ct = data[:12], data[12:]
return AESGCM(base64.b64decode(key_b64)).decrypt(iv, ct, None).decode("utf-8")
Python 的密码模式版本见 AES 加密页面。这类加密适合把一个秘密从一个人传给另一个人;送达之后,请把它转存到密码管理器或密钥管理服务中,不要一直保留密文。
常见问题
没有密码能解密 Base64 吗?
如果只是 Base64,可以,因为根本没有什么需要解密的:把它粘贴到 Base64 解码器即可。如果是以 Base64 表示的 AES 密文,就不行。没有密码或密钥,这些字节无法恢复,也没有任何工具能猜出强密码。
密码明明是对的,为什么提示密码错误?
先检查 PBKDF2 迭代次数:它不保存在密文中,如果发送方改过这个值,必须告诉你具体数字。另外,请确认字符串复制完整,并且确实是本工具格式生成的。
这个工具能解密 OpenSSL 或 CryptoJS 加密的文本吗?
不能。以 U2FsdGVkX1 开头的字符串带有 OpenSSL 的 Salted__ 头部,使用不同的密钥派生方式,通常还是 CBC 模式。请用 openssl enc -d 加上加密时的相同选项来解密。
解密后的文本会被怎样处理?
它只显示在页面上,不会出现在其他任何地方。解密通过 WebCrypto 在你的浏览器中完成,页面的内容安全策略会阻止除 Cloudflare 的浏览量统计以外的所有网络请求,所以密码和消息都不会被发送出去。