必须用cbc(或gcm)而不能用ecb,因为ecb模式下相同明文块生成相同密文块,会暴露数据模式(如图片轮廓、json结构),完全不安全;cbc通过随机iv和密文链式异或彻底消除该风险,是真实场景中可落地的最低安全要求。

直接用 OpenSSL 的 EVP_EncryptInit_ex + EVP_EncryptUpdate 配合 CBC 模式和 PKCS#7 填充,是最稳妥、可落地的选择。自己手写 AES 轮函数或依赖 header-only 库虽可行,但密钥派生、IV 安全生成、错误处理等细节极易出错,不推荐新手从零造轮子。
为什么必须用 CBC(或 GCM)而不能用 ECB
ECB 模式下,相同明文块 → 相同密文块。一张 PNG 图片加密后仍能看清轮廓;一段重复的 JSON 字段会暴露结构。OpenSSL 的 EVP_aes_256_ecb 函数虽然存在,但实际用于文件加密时等于“裸奔”。CBC 通过引入随机 IV 和前一块密文异或,彻底打破这种模式——哪怕整个文件是 0x00,加密后也是完全不可预测的二进制流。
- IV 必须每次加密都重新生成,且随密文一起保存(通常前置在密文文件开头)
- ECB 不需要 IV,但绝不能用于真实文件加密场景
- GCM 模式额外提供认证(AEAD),但 OpenSSL 的 GCM API 对错误处理更敏感,稍有不慎就解密失败
密钥不能直接用字符串,必须派生
用户输入的密码(比如 "MySecret123")不是合法 AES 密钥。AES-256 要求 32 字节密钥,且需具备密码学随机性。直接 memcpy 或 std::string 转 uint8_t* 会导致密钥熵极低,极易被暴力破解。
- 必须用
PKCS5_PBKDF2_HMAC_SHA256对口令 + salt 进行至少 10 万次迭代派生密钥 - salt 不能固定,每次加密都应生成新随机 salt(同样需随密文保存)
- OpenSSL 1.1.1+ 中
EVP_PKEY_CTX_set1_pbe_pass已弃用,坚持用PKCS5_PBKDF2_HMAC
文件读写必须分块,不能一次性 load 全部
大文件(如 2GB 视频)直接 std::ifstream::read 到内存会爆堆,且无法流式加密。OpenSSL 的 EVP 接口天然支持增量处理,关键在于控制缓冲区大小和边界对齐。
- 建议使用 16KB(即 16384 字节)缓冲区:足够避开系统调用开销,又不会占用过多内存
-
EVP_EncryptUpdate可能输出比输入少 1 个 block(因填充逻辑),需用临时 buffer 接收 - 最后一块调用
EVP_EncryptFinal_ex获取剩余填充字节,这部分长度不确定(1–16 字节) - 密文文件 =
[salt(16)][iv(16)][encrypted_data][padding],解密时按此顺序读取
OpenSSL 错误码不报具体原因,必须手动检查
OpenSSL 多数 API 返回 1 表示成功,0 或负数表示失败,但不告诉你哪里错了。ERR_print_errors_fp(stderr) 会输出一堆堆栈,真正有用的只有最末一行。
- 常见陷阱:
EVP_EncryptInit_ex返回 0 → 很可能是密钥长度不对(AES-256 要 32 字节,不是 256 bit 字符串) -
EVP_EncryptFinal_ex失败 → 通常是上下文未正确初始化,或前面某次Update出错后没清空状态 - 务必在每个 EVP 调用后加
if (!ret) { ERR_print_errors_fp(stderr); exit(1); }
真正麻烦的从来不是“怎么调 API”,而是 salt 怎么存、IV 怎么传、错误发生时如何定位、以及密钥派生参数是否够强——这些细节不写死在代码里,光靠文档根本没法复现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











