如何用Crypto++验证Python生成的RSA签名及问题排查
RSA跨语言签名验证失败排查与修复
问题背景
我用Python实现服务端、C实现客户端的RSA签名验证流程:Python服务端持RSA私钥,C客户端持配对公钥。客户端发送字符串到服务端,服务端用私钥生成ASCII格式签名返回,客户端需验证签名的合法性(来自配对密钥、基于目标字符串生成)。但现有代码始终返回false,Crypto++调试难度大,确定只需调整参数即可修复,求问题点。
原始代码与问题点
原始Python服务端代码
import rsa from base64 import b64encode str = "message" pub, priv = rsa.newkeys(2048) # 注:此处存在笔误,privkey应为priv keyB64 = rsa.sign(str.encode('utf-8'), privkey, 'SHA-1') signature = b64encode(keyB64).decode('ascii') with open("public_key.txt", "w") as file: file.write(pub.save_pkcs1().decode('utf8')) file.close()
生成的PKCS#1格式公钥示例
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBALqrXqb17/TiXmGGbvbFwRMV+mbCqPtvnD0zlvIKxpJ4NSBVZ2Lz87SU
Ww69uFILy19G6prThJAzHha9pa3fWRKRv5epMXcP6TFZ3er0h0uaxOKxle+OtpnC
xyW+QMzkhuDL1gR1OrgVW6jCV6lmVdca63+m2PfTjQj1Vc64OyWBAgMBAAE=
-----END RSA PUBLIC KEY-----
原始C++客户端验证代码
#include <./Cryptopp/rsa.h> #include <./Cryptopp/hex.h> #include <./Cryptopp/pssr.h> inline bool RsaVerifyString(const std::string &aPublicKeyStrASCII, const std::string &str, const std::string &aSignatureStrASCII) { // decode and load public key (using pipeline) CryptoPP::RSA::PublicKey publicKey; publicKey.Load(CryptoPP::StringSource(aPublicKeyStrASCII, true).Ref()); // decode signature std::string decodedSignature; CryptoPP::StringSource ss(aSignatureStrASCII, true); // verify message bool result = false; CryptoPP::RSASS<CryptoPP::PSSR, CryptoPP::SHA1>::Verifier verifier(publicKey); CryptoPP::StringSource ss2(decodedSignature + str, true, new CryptoPP::SignatureVerificationFilter(verifier, new CryptoPP::ArraySink((unsigned char*)&result, sizeof(result)))); return result; } //... std::string message("message"); if(RsaVerifyString(publicKeyASCII, message, signatureASCII)) { std::cout << "OK" << std::endl; }
原始代码核心错误点
- Python代码笔误:
rsa.sign的第二个参数是privkey,但生成的私钥变量是priv,直接运行会报错。 - 签名解码缺失:Python用Base64编码签名,但C++代码未添加
Base64Decoder,导致decodedSignature为空。 - 验证输入逻辑错误:Crypto++的
SignatureVerificationFilter需要单独传入消息和签名,而非拼接两者。正确流程是先输入消息,再通过过滤器验证签名。 - 公钥加载方式错误:Python生成的是PKCS#1格式公钥,Crypto++直接调用
Load无法正确解析,需用PEM_Load结合StringSource加载。
更新尝试:迁移到PyCryptoDome后的问题
更新后的Python服务端代码(PyCryptoDome)
from Crypto import Random from Crypto.Hash import SHA from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 PRIV_PATH = '../priv.pem' PUB_PATH= '../pub.pem' def gen_key_pair(): random_generator = Random.new().read key = RSA.generate(2048, random_generator) print(key.exportKey(), key.publickey().exportKey()) with open(PRIV_PATH, 'wb') as file: file.write(key.exportKey()) with open(PUB_PATH, 'wb') as file: file.write(key.publickey().exportKey()) return key.exportKey(), key.publickey().exportKey() def sign_message(message): key = RSA.importKey(open(PRIV_PATH, 'rb').read()) h = SHA.new(message) signer = PKCS1_v1_5.new(key) signature = signer.sign(h) return signature def verify_sign(message, signature): key = RSA.importKey(open(PUB_PATH, 'rb').read()) h = SHA.new(message) verifier = PKCS1_v1_5.new(key) if verifier.verify(h, signature): print("The signature is authentic.") else: print("The signature is not authentic.") # TEST CRYPTO gen_key_pair() message = 'Hello pycrypto!'.encode('utf-8') signature = sign_message(message).hex() print('signature='+signature) verify_sign(message, bytes.fromhex(signature))
预计修改后的C++客户端代码(未整理)
inline bool RsaVerifyString(const std::string &aPublicKeyStrASCII, const std::string &str, const std::string &aSignatureStrASCII) { // decode and load public key (using pipeline) CryptoPP::RSA::PublicKey publicKey; publicKey.Load(CryptoPP::StringSource(aPublicKeyStrASCII, true).Ref()); // decode signature std::string decodedSignature; CryptoPP::StringSource ss(aSignatureStrASCII, true); // verify message bool result = false; CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier(publicKey); CryptoPP::StringSource ss2(decodedSignature + str, true, new CryptoPP::SignatureVerificationFilter(verifier, new CryptoPP::ArraySink((unsigned char*)&result, sizeof(result)))); return result; }
更新后代码的潜在问题
- 哈希算法不匹配:Python用SHA-1(
Crypto.Hash.SHA对应SHA-1),但C++代码中用了SHA256,两者必须一致。 - 签名编码方式不匹配:Python将签名转为Hex字符串,但C++代码未做Hex解码,直接处理ASCII字符串。
- 仍存在原始代码的验证逻辑错误:拼接签名和消息的方式依然错误,公钥加载方式也未修正。
修复后的C++客户端代码示例
#include <./Cryptopp/rsa.h> #include <./Cryptopp/hex.h> #include <./Cryptopp/base64.h> #include <./Cryptopp/pssr.h> #include <./Cryptopp/pem.h> inline bool RsaVerifyString(const std::string &aPublicKeyStrASCII, const std::string &str, const std::string &aSignatureStrASCII) { // 加载PKCS#1格式公钥 CryptoPP::RSA::PublicKey publicKey; CryptoPP::StringSource pubKeySource(aPublicKeyStrASCII, true); CryptoPP::PEM_Load(pubKeySource, publicKey); // 解码Base64格式签名(如果用Hex则替换为HexDecoder) std::string decodedSignature; CryptoPP::StringSource ss(aSignatureStrASCII, true, new CryptoPP::Base64Decoder( new CryptoPP::StringSink(decodedSignature) ) ); // 初始化验证器:对应Python rsa库的PSS填充+SHA1,或PyCryptoDome的PKCS1_v1_5+SHA1 // 如果用PyCryptoDome的PKCS1_v1_5,替换为RSASS<PKCS1v15, SHA1>::Verifier CryptoPP::RSASS<CryptoPP::PSSR, CryptoPP::SHA1>::Verifier verifier(publicKey); bool result = false; // 正确的验证流程:先输入消息,再通过过滤器验证签名 CryptoPP::StringSource ss2(str, true, new CryptoPP::SignatureVerificationFilter( verifier, new CryptoPP::ArraySink(reinterpret_cast<unsigned char*>(&result), sizeof(result)), CryptoPP::SignatureVerificationFilter::PUT_RESULT | CryptoPP::SignatureVerificationFilter::SIGNATURE_AT_END ) ); // 写入签名到过滤器 ss2.Put(reinterpret_cast<const unsigned char*>(decodedSignature.data()), decodedSignature.size()); ss2.MessageEnd(); return result; }
内容的提问来源于stack exchange,提问作者user325962
相关产品推荐
相关产品推荐

