如何用密码加密文本
- 保持选中 加密 和 密码 密钥类型。
- 输入 密码。勾选 显示密码 可以在输入时看到它。除非有特别的理由,PBKDF2 迭代次数 保持 310,000 即可;解密的人必须填入相同的数字。
- 在 明文 中输入消息,然后点击 加密,或在密码框中按回车,或在文本框中按 Ctrl+Enter。
- 复制或下载 加密的 Base64。如果字符串要放进 URL,请勾选 URL 安全输出(- _,无 =)。
输出下方会显示明细:盐值、IV、密文和标签的大小、总计,以及所用的密钥派生方式。发送结果时,把 Base64 放在一条消息里,把密码通过另一个渠道发送,比如打电话或用另一个聊天应用。接收方打开 AES 解密,按相反的步骤操作即可。
这个工具的用途
当没有更好的渠道时,这个工具可以用来安全地分享一段文本:给访客的 Wi-Fi 密码、发给同事的恢复码、贴进一张太多人能看到的工单里的备注。它让“我直接在聊天里发给你”变成窃听者读不懂的内容。
它不是密钥管理服务。它不存储任何东西,不控制谁有访问权限,不记录谁解密了什么,不轮换密钥,也无法撤回已发出的秘密。应用凭据、生产环境密钥以及团队共享的任何内容,都应该放在正规的密钥管理服务或支持共享的密码管理器中。还要记住,加密后的文本永远可以被日后得知密码的任何人解密。
精确的格式
密码模式下的输出为:
Base64( salt[16] ‖ iv[12] ‖ ciphertext ‖ tag[16] )
- 密钥:对以 UTF-8 编码的密码执行 PBKDF2-HMAC-SHA256,使用 16 字节盐值和 310,000 次迭代,生成 32 字节。
- 加密算法:AES-256-GCM,使用 12 字节 IV、128 位标签,无附加数据。
- 随机性:每次加密都通过
crypto.getRandomValues生成新的盐值和 IV。
密文与 UTF-8 消息的长度完全相同,所以整个数据有 44 字节的开销。33 字节的消息变成 77 字节,也就是 104 个 Base64 字符。数据中没有版本字节,也不保存迭代次数:如果你改了迭代次数,接收方必须填入相同的值,否则解密会失败,报错与密码错误时相同。
在 原始密钥 模式下没有 PBKDF2 这一步,输出为 Base64( iv[12] ‖ ciphertext ‖ tag[16] ),使用你以 Base64 或 hex 形式提供的 16、24 或 32 字节密钥加密。生成随机密钥 可以创建一个。Chrome 和 Edge 不支持 192 位 AES 密钥,工具会明确提示,而不是悄悄失败;请使用 128 位或 256 位。
在 Python 或 Node.js 中解密
接收方不需要这个网站。使用 Python 和 cryptography 包(pip install cryptography):
import base64, os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
def _key(password, salt):
return PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt,
iterations=310000).derive(password.encode("utf-8"))
def decrypt(b64, password):
data = base64.b64decode(b64)
salt, iv, ct = data[:16], data[16:28], data[28:]
return AESGCM(_key(password, salt)).decrypt(iv, ct, None).decode("utf-8")
def encrypt(text, password):
salt, iv = os.urandom(16), os.urandom(12)
ct = AESGCM(_key(password, salt)).encrypt(iv, text.encode("utf-8"), None)
return base64.b64encode(salt + iv + ct).decode("ascii")
AESGCM.decrypt 要求标签位于密文末尾,而本格式正是把它放在那里。在 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");
}
本页生成的数据可以用这两个函数解密,encrypt() 生成的数据也可以在本页解密。如果你使用了 URL 安全输出,Node 的 Buffer.from(b64, "base64") 可以直接读取;在 Python 中请使用 base64.urlsafe_b64decode(b64 + "=" * (-len(b64) % 4))。注意,openssl enc 在这里帮不上忙,因为它不支持 GCM。Python 和 Node.js 页面更深入地介绍了 Base64 这一部分。
常见问题
用 Base64 编码足以保护文本吗?
不够。Base64 只改变字节的书写方式,任何人不需要密钥就能解码。本工具先用 AES 加密,Base64 只是用来把密文变成可以粘贴的文本。详见 Base64 不是加密。
为什么同样的文本每次加密的结果都不一样?
每次运行都会使用新的随机 16 字节盐值和 12 字节 IV。这是有意为之:相同的消息不能产生相同的密文,而且 AES-GCM 绝不能在同一个密钥下重复使用 IV。
密码需要多强?
310,000 次迭代的 PBKDF2 能减慢猜测速度,但救不了过短或常见的密码。请使用较长的随机口令,例如五个或更多随机单词,并通过与密文不同的渠道发送它。
能用这个工具加密文件吗?
不能,它只加密文本。加密文件请使用 age 或 gpg 等专门工具。
工具使用哪种 AES 模式和密钥长度?
密码模式下使用 GCM 模式的 AES-256,认证标签为 128 位。原始密钥模式下,密钥长度取决于你提供的密钥:128、192 或 256 位。