如何基于OpenSSL实现DER编码RSA公钥加密并完成Python到C++移植?
使用OpenSSL实现RSA公钥加密(对应Python Crypto逻辑)
刚好我之前也做过类似的Python到C++的OpenSSL移植,结合你的需求,咱们一步步对应实现逻辑:
1. 导入服务器返回的二进制公钥
你的Python代码里key_bin是服务器发的二进制公钥串,大概率是ASN.1 DER格式(Python的importKey默认支持DER/PEM)。OpenSSL里我们需要分两种情况解析:如果是标准X.509公钥格式(更常见)用d2i_PUBKEY,如果是原始RSAPublicKey格式用d2i_RSAPublicKey,下面是兼容两种格式的导入函数:
#include <openssl/rsa.h> #include <openssl/err.h> #include <vector> #include <cstring> RSA* import_rsa_public_key(const std::vector<unsigned char>& key_bin) { const unsigned char* ptr = key_bin.data(); // 先尝试解析X.509标准公钥格式 EVP_PKEY* pkey = d2i_PUBKEY(nullptr, &ptr, key_bin.size()); if (pkey) { RSA* rsa = EVP_PKEY_get1_RSA(pkey); EVP_PKEY_free(pkey); return rsa; } // 失败则尝试原始RSAPublicKey格式 ptr = key_bin.data(); RSA* rsa = d2i_RSAPublicKey(nullptr, &ptr, key_bin.size()); if (!rsa) { ERR_print_errors_fp(stderr); return nullptr; } return rsa; }
2. 处理RSA填充(核心兼容点)
你的Python代码里有自定义的add_rsa_padding,这是最容易出问题的地方,必须保证C++端的填充逻辑和Python完全一致:
情况1:用OpenSSL内置填充(比如PKCS#1 v1.5)
如果你的add_rsa_padding实现的是标准PKCS#1 v1.5,那直接用OpenSSL内置的填充即可,不用手动处理:
std::vector<unsigned char> rsa_encrypt_with_padding(RSA* rsa, const std::vector<unsigned char>& data) { int rsa_key_len = RSA_size(rsa); std::vector<unsigned char> encrypted(rsa_key_len); // RSA_PKCS1_PADDING对应Python Crypto的默认加密填充 int ret = RSA_public_encrypt(data.size(), data.data(), encrypted.data(), rsa, RSA_PKCS1_PADDING); if (ret == -1) { ERR_print_errors_fp(stderr); return {}; } encrypted.resize(ret); // 截取实际加密有效长度 return encrypted; }
情况2:自定义填充逻辑
如果你的add_rsa_padding是自定义规则,那先手动复刻填充逻辑,再用无填充模式加密:
// 完全复刻你Python里的add_rsa_padding逻辑 std::vector<unsigned char> add_rsa_padding(const std::vector<unsigned char>& data, int rsa_key_len) { std::vector<unsigned char> padded(rsa_key_len); // 这里写你Python里的填充规则,比如示例的PKCS#1 v1.5模拟: padded[0] = 0x00; padded[1] = 0x02; // 加密块标识 // 填充随机非零字节(和Python逻辑一致) for (int i = 2; i < rsa_key_len - data.size() - 1; ++i) { padded[i] = rand() % 255 + 1; // 保证非零 } padded[rsa_key_len - data.size() - 1] = 0x00; // 复制原始数据到末尾 std::memcpy(&padded[rsa_key_len - data.size()], data.data(), data.size()); return padded; } // 无填充模式加密 std::vector<unsigned char> rsa_encrypt_no_padding(RSA* rsa, const std::vector<unsigned char>& padded_data) { int rsa_key_len = RSA_size(rsa); if (padded_data.size() != rsa_key_len) { fprintf(stderr, "Padded data length mismatch!\n"); return {}; } std::vector<unsigned char> encrypted(rsa_key_len); int ret = RSA_public_encrypt(rsa_key_len, padded_data.data(), encrypted.data(), rsa, RSA_NO_PADDING); if (ret == -1) { ERR_print_errors_fp(stderr); return {}; } return encrypted; }
3. 完整流程示例(对应你的Python代码)
把所有步骤串起来,和你的Python逻辑一一对应:
int main() { // 初始化OpenSSL错误处理 OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); // 1. 获取服务器公钥(对应你的get_server_key()) std::vector<unsigned char> key_bin = get_server_key(); // 2. 导入公钥 RSA* rsa = import_rsa_public_key(key_bin); if (!rsa) { fprintf(stderr, "Failed to load RSA public key!\n"); return 1; } // 3. 准备要加密的原始数据 std::vector<unsigned char> data = {...}; // 你的业务数据 // 4. 加密(二选一,根据填充方式) std::vector<unsigned char> encrypted_data; // 用内置填充的情况: encrypted_data = rsa_encrypt_with_padding(rsa, data); // 用自定义填充的情况: // int rsa_len = RSA_size(rsa); // auto padded = add_rsa_padding(data, rsa_len); // encrypted_data = rsa_encrypt_no_padding(rsa, padded); // 5. 发送加密后的数据(对应你的send_data()) send_data(encrypted_data); // 清理OpenSSL资源,避免内存泄漏 RSA_free(rsa); EVP_cleanup(); ERR_free_strings(); return 0; }
关键注意点
- 密钥格式排查:如果公钥导入失败,把
key_bin转成十六进制打印,对比Python里的格式——如果包含-----BEGIN PUBLIC KEY-----这类字符串,那是PEM格式,需要用PEM_read_bio_RSAPublicKey解析。 - 填充一致性:这是加密后服务器能否解密的核心!一定要逐行对比Python和C++的填充逻辑,比如填充字节的规则、长度计算。
- 资源清理:OpenSSL的结构体(
RSA*、EVP_PKEY*)必须手动释放,否则会有内存泄漏。
内容的提问来源于stack exchange,提问作者William Chapa
相关产品推荐
相关产品推荐

