crypto/aes 包仅提供底层块加密器,无法直接安全加解密:需手动处理pkcs#7填充、随机iv生成、cbc模式封装及填充校验,否则易panic或解密乱码;推荐优先使用aead模式。

直接用 crypto/aes 包无法安全加解密——它只提供块加密器,必须手动补位、生成 IV、组合模式、封装密文,否则大概率 panic 或解密乱码。
为什么 aes.NewCipher 不能直接加密字符串
aes.NewCipher 返回的是一个纯块加密器(cipher.Block),它只对恰好 aes.BlockSize(16 字节)的输入做一次 AES 变换。传入 "hello" 这种非 16 字节明文会 panic;传入未填充的 UTF-8 字符串(如含中文)更易因长度不对齐而静默截断。
- 密钥长度必须是 16 / 24 / 32 字节:用
[]byte("1234567890123456")可以,但[]byte("mypass")不行 -
block.Encrypt(dst, src)要求len(dst) == len(src) == block.BlockSize(),且dst必须已分配内存 - 不处理填充、IV、编码——这些全得你写,漏一步就不可逆
如何正确实现 CBC 模式加解密
CBC 是最常用也相对可控的模式,但三要素缺一不可:PKCS#7 填充 + 随机 IV + cipher.BlockMode 封装。
- 填充必须严格:末尾补
n字节,每个值都是n,其中n = 16 - len(data)%16 - IV 必须每次加密都重新生成:
iv := make([]byte, aes.BlockSize)+io.ReadFull(rand.Reader, iv)(不能用rand.Read(iv),可能读不满) - 密文结构推荐:
iv + encrypted(前置 IV),解密时先切出前 16 字节再初始化NewCBCDecrypter - 加密后密文字节数 =
16 + len(padded_data),不是原始字符串长度
解密失败时为什么没报错,却全是乱码
cipher.NewCBCDecrypter 的 CryptBlocks 方法永远不会返回 error,哪怕密钥错、IV 错、密文损坏或被篡改。它只是机械地执行异或和解密轮运算,输出完全不可控的字节流。
- 解密后必须显式校验 PKCS#7 填充:取最后一个字节
n,检查是否在1..16范围内,且末尾n字节是否全等于n - 填充校验失败应返回 error,而不是直接
string(decrypted)—— 否则传给json.Unmarshal可能 panic - 若业务允许,优先用 AEAD 模式(如
chacha20poly1305),它自带完整性校验,解密失败会明确报错
文件加密时最容易忽略的两个硬伤
大文件场景下,仅靠 ioutil.ReadFile + 内存加密会爆内存;而盲目套 io.Copy 又会破坏块对齐,导致最后一块解密失败。
- 流式加密必须分块处理:缓冲区大小建议设为 64KB(需是 16 的倍数),每块单独填充 → 加密 → 写入
- IV 和 salt(若用 scrypt 派生密钥)必须随密文持久化:标准做法是密文开头固定位置存 salt(如 32 字节)、接着 IV(16 字节)、再密文主体
- 别把密钥硬编码进代码;生产环境应通过环境变量或 KMS 注入,且避免用
sha256等快速哈希代替scrypt.Key











