最稳妥路径是直接调用evp_encryptinit_ex、evp_encryptupdate、evp_encryptfinal_ex;需严格匹配密钥/iv长度、随机iv、pkcs#7填充、base64编解码及evp_cipher_ctx_free释放。

怎么用 OpenSSL 的 EVP 接口做 AES 加密
直接调用 EVP_EncryptInit_ex、EVP_EncryptUpdate、EVP_EncryptFinal_ex 是最稳妥的路径,比手撸 ECB/CBC 模式拼接更安全,也比旧的 EVP_CIPHER_CTX_new + 手动管理生命周期更少出错。
常见错误是传错密钥长度或 IV 长度——AES-128 要 16 字节密钥 + 16 字节 IV,AES-256 则要 32 字节密钥;IV 必须随机且每次加密不同,不能硬编码或复用。
- 用
RAND_bytes生成 IV,别用time()或计数器 - 密钥建议用 PBKDF2(
EVP_PBE_scrypt或PKCS5_PBKDF2_HMAC)从口令派生,别直接当字符串用 - 输出密文前记得调用
EVP_EncryptFinal_ex补齐 padding,否则末尾一两个字节会丢
为什么必须手动处理 padding 和 base64 编码
OpenSSL 的 EVP 接口默认启用 PKCS#7 填充,但只作用于加密过程内部;它不负责把二进制密文转成可传输的字符串。如果你直接把 unsigned char* 当 C 字符串打印,遇到 \0 就截断,解密时必然失败。
典型现象:加密后看起来“成功”,但解密返回 0,或者报 bad decrypt 错误——大概率是密文被截断或编码损坏。
立即学习“C++免费学习笔记(深入)”;
- 加密后用
EVP_EncodeBlock转 base64,别用第三方轻量 encoder(可能缺结尾=) - 解密前先用
EVP_DecodeBlock还原二进制,注意它会忽略换行和空格,但要求输入长度是 4 的倍数 - 别在 base64 中间插入换行符,除非你明确控制了
EVP_ENCODE_LENGTH并跳过换行逻辑
解密失败常见原因和调试方法
最常卡在 EVP_DecryptFinal_ex 返回 0,这时候不要急着改算法,先确认三件事:密钥完全一致、IV 完全一致、密文完整未损坏。
一个有效调试技巧:把加密后的密文、IV、密钥长度全打出来十六进制,和解密端逐字节比对。哪怕差一个字节,AES-CBC 就整个块解不出来。
- 检查
EVP_DecryptInit_ex是否传了和加密时完全相同的 cipher(比如EVP_aes_128_cbc()),别混用_ecb()和_cbc() - 确认解密前没意外修改 IV 缓冲区(例如把它当字符串用
strlen算长度) - 如果用了自定义 padding(如 no-padding),必须在 init 时显式关掉:
EVP_CIPHER_CTX_set_padding(ctx, 0)
C++ 里怎么安全释放 OpenSSL 资源
不是所有对象都用 delete,也不是所有函数都配对调用 _free。EVP 接口多数靠 EVP_CIPHER_CTX_free 收尾,但 IV 和密钥内存是你自己 malloc 或 stack 分配的,得自己管。
容易踩的坑是重复 free 或漏 free EVP_CIPHER_CTX*,导致 ASan 报 use-after-free,或者长期运行时内存缓慢增长。
- 初始化用
EVP_CIPHER_CTX_new(),结束必须配EVP_CIPHER_CTX_free(ctx),别用delete ctx - 如果用
std::vector<unsigned char></unsigned>存 IV 或密文,就不用手动free,但 vector 要 move 出去再用,避免拷贝时意外截断 - 多线程下别共用同一个
EVP_CIPHER_CTX*,每个线程该 new 就 new,别图省事全局 static










