必须用kdf派生密钥而非直接使用密码,因用户密码熵低(如"p@ssw0rd123"仅约74位),远低于aes-256所需的256位高熵密钥,易被gpu暴力破解;go中需用golang.org/x/crypto/pbkdf2并正确设置salt、iterations≥100000、keylen=32、hashfunc=sha256.new。

直接用 password 当 AES 密钥是错的,必须走 KDF 派生——否则暴力破解只要几小时。
为什么不能把字符串密码直接当密钥用
用户输的 "P@ssw0rd123" 只有约 74 位熵,而 AES-256 要求 256 位高熵密钥。攻击者用 GPU 集群跑 PBKDF2 破解这种密码,平均耗时不到一天。Go 标准库不会帮你拦这个错误,aes.NewCipher([]byte("P@ssw0rd123")) 能编译能运行,但等于裸奔。
常见错误现象:
- 加密后文件能解密,但换一台机器或改一次代码就失败
- 同一密码在不同版本程序中生成密钥不一致
- 用
sha256.Sum256([]byte(pw)).Sum(nil)截 32 字节,结果被 rainbow table 批量撞库
golang.org/x/crypto/pbkdf2 是最稳妥的生产选择
它符合 RFC 2898,迭代次数可调,抗 GPU/ASIC 暴力,Go 官方维护,和主流语言互通性好。别自己写循环哈希。
关键参数说明:
-
salt:必须每次加密都调用crypto/rand.Read()生成新值(推荐 32 字节),和密文一起存,不能复用 -
iterations:≥ 100000(2026 年基准),CPU 占用可控,但能拖慢暴力速度两个数量级 -
keyLen:AES-256 要 32,别写错成 16 或 24 -
hashFunc:固定用sha256.New,别用 md5/sha1
示例片段:
salt := make([]byte, 32) _, _ = rand.Read(salt) key := pbkdf2.Key([]byte(password), salt, 100000, 32, sha256.New)
HKDF 更适合密钥分层场景(比如从主密钥派生多个子密钥)
如果你不是处理用户口令,而是已有高熵密钥(如 ECDH 共享密钥、硬件 dongle 输出),HKDF 比 PBKDF2 更轻量、更快、更灵活。
典型用法:
-
HKDF-Extract:用 salt 把原始密钥“拧干”,输出均匀随机的 PRK -
HKDF-Expand:用info字段区分用途,比如"aes-key"和"hmac-key",避免密钥复用
注意:HKDF 不抗弱口令,info 是公开的,salt 可选但建议填非零值。
依赖包:golang.org/x/crypto/hkdf
最容易被跳过的协议细节:salt、IV、校验和三者的偏移必须固化
哪怕 KDF 逻辑完全正确,如果加密端把 salt 写在文件头前 32 字节、IV 紧跟其后 16 字节,而解密端误读成前 16 字节是 IV、后面 32 字节是 salt,就会密钥错位、解密出乱码且不报错。
真实踩坑点:
- 没在文档里明确定义文件格式头部结构
- 测试时用同一进程加解密,salt/IV 没落盘,误以为“通了”
- 换语言实现(如 Python/JS)时,字节序或填充方式不一致
建议:用固定 magic header + version 字段开头,再按协议顺序写 salt/IV/ciphertext/checksum,解密时先校验 header 和长度。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











