C++ OpenSSL AES加密后,Python/PHP解密开头乱码问题求助
问题描述
使用C++的OpenSSL实现AES加密后,用Python的Crypto.Cipher AES库或PHP的OpenSSL解密时,解密结果开头出现随机二进制乱码。例如原明文:{"pass":"azertyu","opt":...},解密后变成:"Ž{mÂ┬X▒ Íł.Q>4","opt":...},推测是填充问题但无法定位。
Python解密代码
import binascii from Crypto.Cipher import AES from Crypto.Util.Padding import unpad def decrypt(ciphertext_hex): key = b"t*H3_B3$t/k$%evr" data = binascii.unhexlify(ciphertext_hex) iv = data[:AES.block_size] data = data[AES.block_size:] cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = unpad(cipher.decrypt(data), AES.block_size) print(decrypted) # return decrypted.decode("utf-8", errors="replace") return decrypted.decode("utf-8")
C++加密代码
#include <iostream> #include <sstream> #include <iomanip> #include <cstring> #include <openssl/aes.h> #include <openssl/rand.h> #include <openssl/crypto.h> std::string binToHex(const std::string &input) { std::ostringstream hexStream; hexStream << std::hex << std::setfill('0'); for (unsigned char c : input) { hexStream << std::setw(2) << static_cast<int>(c); } return hexStream.str(); } std::string crypt(const std::string &data, const std::string &key) { const unsigned char *keyBytes = reinterpret_cast<const unsigned char *>(key.c_str()); const unsigned char *dataBytes = reinterpret_cast<const unsigned char *>(data.c_str()); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); AES_KEY aesKey; if (AES_set_encrypt_key(keyBytes, 128, &aesKey) < 0) { std::cout << "key"; return ""; } unsigned char iv[AES_BLOCK_SIZE]; if (RAND_bytes(iv, AES_BLOCK_SIZE) != 1) { std::cout << "rand bytes"; return ""; } size_t paddedLen = (data.length() + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE; unsigned char *paddedData = new unsigned char[paddedLen]; std::memcpy(paddedData, dataBytes, data.length()); memset(paddedData + data.length(), paddedLen - data.length(), paddedLen - data.length()); unsigned char ciphertext[paddedLen]; AES_cbc_encrypt(paddedData, ciphertext, paddedLen, &aesKey, iv, AES_ENCRYPT); std::string output(reinterpret_cast<char *>(iv), AES_BLOCK_SIZE); output.append(reinterpret_cast<char *>(ciphertext), paddedLen); delete[] paddedData; return binToHex(output); }
问题原因与解决方法
核心问题:填充规则不匹配
C++代码手动实现的填充逻辑和Pythonunpad函数默认的PKCS#7填充不兼容:
- 当明文长度刚好是AES块大小(16字节)的整数倍时,你的C++代码没有添加完整的一个块填充(PKCS#7要求此时必须填充16个值为0x10的字节)。
- Python的
unpad严格遵循PKCS#7规则,填充不匹配会导致解密时块偏移错误,最终表现为开头乱码。
修复步骤
修正C++的填充逻辑,严格实现PKCS#7:
// 替换原填充相关代码 size_t paddingLen = AES_BLOCK_SIZE - (data.length() % AES_BLOCK_SIZE); size_t paddedLen = data.length() + paddingLen; unsigned char *paddedData = new unsigned char[paddedLen]; std::memcpy(paddedData, dataBytes, data.length()); memset(paddedData + data.length(), paddingLen, paddingLen);该逻辑会自动处理两种情况:
- 明文长度不是块倍数:填充
paddingLen个值为paddingLen的字节 - 明文长度是块倍数:填充16个0x10字节(符合PKCS#7要求)
- 明文长度不是块倍数:填充
验证Python端代码
确保使用Crypto.Util.Padding.unpad(默认PKCS#7),无需修改,修正C++填充后即可正常解密。额外注意点
- C++中如果明文是JSON等不含
\0的字符串,建议用data.data()替代data.c_str()获取字节(c_str()会自动添加\0终止符,可能导致明文长度错误)。 - iv与密文的拼接、拆分逻辑是正确的,无需调整。
- C++中如果明文是JSON等不含
内容的提问来源于stack exchange,提问作者Ключник
相关产品推荐
相关产品推荐

