openssl推荐使用evp_encryptinit_ex等高层接口进行aes加密,需明确指定evp_aes_128_cbc()等算法、随机生成并保存iv、用pbkdf2派生密钥、二进制模式读写文件、用vector而非string存储密文,避免bad decrypt错误。

怎么用 OpenSSL 的 EVP 接口做 AES 加密(C++)
直接用 AES_encrypt 这类底层函数容易出错,OpenSSL 官方推荐走 EVP_EncryptInit_ex 这套高阶接口——它自动处理 padding、IV 生成、密钥派生等细节,且支持多算法切换。
关键不是“能不能”,而是“不按这个流程做,大概率解密失败或触发 bad decrypt 错误”。
- 必须用
EVP_aes_128_cbc()或EVP_aes_256_cbc()明确指定模式,别依赖默认值;CBC 比 ECB 安全,ECB 已被弃用 -
IV必须随机生成(用RAND_bytes()),且和密文一起保存——解密时缺 IV 就会解出乱码 - 密钥不能直接用字符串,得用
EVP_BytesToKey()或 PBKDF2(PKCS5_PBKDF2_HMAC_SHA1)派生,否则短密码强度极低 - 加密后数据长度 ≠ 原文件长度:CBC 模式会补位,最终大小是
((len + 15) / 16) * 16
读写二进制文件时踩的坑(fopen / std::fstream)
用文本模式打开加密文件,Windows 下会把 \x1a 当 EOF,Linux 下可能没事但跨平台必崩。必须强制二进制模式。
- C 风格:用
fopen("file.bin", "rb")和fopen("out.enc", "wb"),"r"或"w"都不行 - C++ 风格:
std::ifstream f("in.bin", std::ios::binary),漏掉std::ios::binary标志,read()会提前截断 - 别用
std::string存密文——它内部假设末尾是\0,而密文里可能有多个\0,改用std::vector<uint8_t></uint8_t>
解密失败常见报错和定位方法
最常遇到的是 bad decrypt(OpenSSL 报错)或解出来全是乱码,90% 是以下三个原因。
立即学习“C++免费学习笔记(深入)”;
- 加密和解密用的
key或iv不一致——检查是否把 IV 当作固定值硬编码,或没从密文头部正确读取 - 加解密模式不匹配:比如加密用
EVP_aes_128_cbc(),解密却传了EVP_aes_128_ecb() - padding 处理错误:调用
EVP_DecryptFinal_ex()前没确保输入长度是块对齐的(16 字节倍数),会导致返回 -1 - 调试技巧:先用已知 key/iv 加密一段固定明文(如
"hello"),再用相同参数解密,确认流程通了再换文件
要不要自己实现 AES 轮函数?
不要。OpenSSL、Crypto++、libsodium 都经过严格审计,自己手写 AES_encrypt 或 S-box 查表,既没性能优势,又极易引入侧信道漏洞(如时序差异暴露密钥)。
真正要花时间的地方是:密钥怎么安全存储(别写死在代码里)、IV 怎么传输、加密后是否校验完整性(AES 本身不防篡改,得加 HMAC 或用 GCM 模式)。
这些比“怎么调用 AES 函数”重要得多,但恰恰最容易被跳过。










