必须用OpenSSL EVP接口而非低层AES函数,因其自动处理PKCS#7填充、校验IV/密钥长度、安全清零敏感数据,且调用流程规范、错误明确、可维护性强。

别用 AES_ecb_encrypt,也别手撸 AES_cbc_encrypt 直接套用——OpenSSL 的 EVP 接口才是 C++ 工程中真正能落地、可维护、不出哑巴错的路径。
为什么必须用 EVP 接口而不是低层 AES 函数
低层函数(如 AES_cbc_encrypt)不自动处理填充、不校验 IV 长度、不隔离密钥生命周期,且 ivec 参数在加密后会被原地修改——解密前若没重置,EVP_DecryptFinal_ex 必然返回 0。EVP 接口把 cipher、key、IV、padding 全部封装进 EVP_CIPHER_CTX,调用顺序固定、错误码明确、资源释放路径清晰。
-
EVP_EncryptInit_ex负责初始化并绑定 cipher / key / IV,自动校验长度匹配(比如传EVP_aes_256_cbc()却给 16 字节密钥会直接失败) - 它默认启用 PKCS#7 填充,无需手动补零或计算 block size
-
EVP_CIPHER_CTX_free会安全清零内存中的密钥和 IV,而delete或free不会
密钥和 IV 怎么生成才安全
硬编码字符串、用 time(0) 当 IV、把口令字符串直接当密钥——这三类写法在调试时看似“能跑”,上线后就是明文裸奔。
- IV 必须每次加密都调用
RAND_bytes(iv, 16)生成,不能复用,不能截断,不能用strlen算长度(iv是二进制缓冲区,不是 C 字符串) - 密钥若来自用户口令,必须用
PKCS5_PBKDF2_HMAC或EVP_PBE_scrypt派生,至少 10 万次迭代,盐值随机生成并随密文存储 - 若密钥是预共享的二进制密钥(如硬件注入),确保长度精确为 16 / 24 / 32 字节,别用
sizeof("1234567890123456")——那是 17(含 \0)
base64 编码和 padding 的坑在哪
最常卡在“加密成功但解密报 bad decrypt”,90% 是因为密文被截断或 base64 损坏,而非算法配错。
立即学习“C++免费学习笔记(深入)”;
- 加密输出是 raw binary,直接
printf("%s", ciphertext)遇到 \0 就停,解密端拿到的是残缺数据 - 必须用
EVP_EncodeBlock(out_b64, ciphertext, cipher_len),别用std::base64_encode等第三方实现——它们可能省略结尾=,导致EVP_DecodeBlock解码失败 -
EVP_EncodeBlock输出长度是4 * ((len + 2) / 3),要提前 malloc 足够空间,别传栈上固定大小数组(如char b64[256])硬塞 - 解密前用
EVP_DecodeBlock还原二进制,它会跳过空格换行,但输入长度必须是 4 的倍数;若 base64 末尾缺=,先补上再 decode
解密失败时优先检查哪三件事
别一上来就怀疑 cipher 模式或 OpenSSL 版本——先盯死这三个字节级事实:
- 把加密端输出的密钥、IV、密文全用
printf("%02x", byte)打成十六进制,和解密端收到的逐字节比对;差一个字节,EVP_DecryptFinal_ex就静默返回 0 - 确认
EVP_DecryptInit_ex传的 cipher 和加密时完全一致,比如加密用EVP_aes_128_cbc(),解密就不能传EVP_aes_128_ecb() - 检查 IV 缓冲区是否被意外当字符串用了——例如
strlen((char*)iv)会提前截断,导致传给EVP_DecryptInit_ex的 IV 只有前几个字节
CFB 模式虽免填充,但 IV 复用后果更隐蔽:解密结果前 16 字节全错,后面才逐渐恢复——这种错误模式比 CBC 的“整块乱码”更难定位。


















