如何用OpenSSL C++验证PyCryptoDome生成的RSA签名?
OpenSSL验证PyCryptoDome签名失败的问题排查与解决
问题背景
使用PyCryptoDome生成RSA签名,在C中用OpenSSL验证始终失败。公钥已确认有效(用同一私钥在C端签名可验证通过),怀疑问题出在签名格式或编解码流程上。
Python生成签名代码
from Crypto import Random from Crypto.PublicKey import RSA from Crypto.Hash import SHA256 from Crypto.Signature import PKCS1_v1_5 import base64 RSA_BITS = 2048 PRIV_PATH = 'priv.pem' PUB_PATH= 'pub.txt' def gen_key_pair(): random_generator = Random.new().read key = RSA.generate(RSA_BITS, random_generator) priv = key.exportKey() pub = str(key.publickey().exportKey()) # 此处存在潜在格式问题 with open(PRIV_PATH, 'wb') as file: file.write(priv) with open(PUB_PATH, 'w') as file: file.write(pub) print("Public key: "+pub+'\n') def sign_message(message): key = RSA.importKey(open(PRIV_PATH, 'rb').read()) signer = PKCS1_v1_5.new(key) h = SHA256.new(message.encode('ascii')) print('Signature: '+base64.b64encode(signer.sign(h)).decode('ascii')) gen_key_pair() sign_message('hello')
C++验证核心代码(问题集中在Base64解码)
#include <iostream> #include <openssl/evp.h> #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/bio.h> #include <QCoreApplication> std::string PUBLIC_KEY ="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dwgfS3AciOd1yZ2U1HY\n0/sneN6ciARRzKtgHz7EOymoFANuF8MpYWNx8l+SqUfr/Ii2bAOC+94L3hD4Dqw3\nfwDH8vDOjOvCEzgrMdAXZ+Nv8cwRmVlYW8qu5OqXaN7Dv1Scf9vBtCSO59WJ2V4l\nk3VnpjrBqpjJAfW+JNIqsWXBLErOkTtcljBxBADXFVQIBCyTz3QbN+UP7g0eoBQI\n8x8tzmHg7L5vM36Iepn3EIdBBWD942CfqeA0emcFixBtAD7wsg96f9e2Z1tBLskN\nJHRNLgAX50OSuZzMebC9aWV0Z2VhyfCiZIfugODe6X8CXNNTzMqa10eGtXx65kcn\nvwIDAQAB\n-----END PUBLIC KEY-----\n"; std::string SIGNATURE = "a+lpWth2RQG21cODvF0SNtpHuQZKPzCB1geo67EywOPtR2bYVkAcQ99+a8thmzfosXgRVQWjCMDnGJmSwnQUywhMxahZri0WRp/NhEEBrbXV1BxGZcL6uhfGJ4aAds9vWO+fIrE86rUQI9YZlU3griq1DRrzgsRWMviMwflqTskY1F85FpmcCLD1UCMJ9pYHYtRCsHBtNX4PnqR5dzfhkSSmVruOXyV0h4foK/m4+Ad2Xkz8VYZxVjq/64k2vqSuPPBhv3Saizvczq4/wP5IKYUjei/UcMau2g0TUzMXzPOXrVaKsXVDGjUlil6uwYGQ3zQFUIL95rpRYCKsAf8AGQ=="; std::string MESSAGE = "hello"; // 问题函数:Base64解码 void Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { BIO *bio, *b64; int decodeLen = calcDecodeLength(b64message); *buffer = (unsigned char*)malloc(decodeLen + 1); (*buffer)[decodeLen] = '\0'; bio = BIO_new_mem_buf(b64message, -1); b64 = BIO_new(BIO_f_base64()); bio = BIO_push(b64, bio); *length = BIO_read(bio, *buffer, strlen(b64message)); BIO_free_all(bio); } // 省略其他辅助函数与main函数
问题根源
- Base64解码不兼容无换行格式:OpenSSL的
BIO_f_base64默认要求Base64字符串每行64字符,而Python输出的签名是连续无换行的Base64,导致解码不完整,签名数据长度错误。 - Python公钥导出的格式隐患:
str(key.publickey().exportKey())会将bytes对象转为带b'前缀和'后缀的字符串,复制时若误包含这些字符会导致公钥加载失败。
修复步骤
1. 修正C++的Base64解码函数
添加BIO_FLAGS_BASE64_NO_NL标志,允许解码无换行的连续Base64字符串:
void Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { BIO *bio, *b64; int decodeLen = calcDecodeLength(b64message); *buffer = (unsigned char*)malloc(decodeLen + 1); (*buffer)[decodeLen] = '\0'; bio = BIO_new_mem_buf(b64message, -1); b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); // 关键:关闭换行要求 bio = BIO_push(b64, bio); *length = BIO_read(bio, *buffer, strlen(b64message)); BIO_free_all(bio); }
2. 修正Python的公钥导出逻辑
直接将公钥bytes解码为ASCII字符串,避免生成带b'的格式:
pub = key.publickey().exportKey().decode('ascii') # 替换原str(...)写法
3. 验证签名复制完整性
确保从Python输出复制的签名是完整的,没有截断或多余字符(如空格、换行)。
4. 可选:添加错误日志定位问题
在OpenSSL调用失败的分支添加错误打印,方便排查:
#include <openssl/err.h> // 在EVP_DigestVerifyInit等失败后添加 ERR_print_errors_fp(stderr);
验证结果
修改后重新生成公钥和签名,复制到C++代码中编译运行,即可成功验证签名,输出"Authentic"。
内容的提问来源于stack exchange,提问作者user325962
相关产品推荐
相关产品推荐

