如何在C++中解密C#通过RSA加密的字节数组密文
RSA PKCS#1 v1.5跨C#与Crypto++解密问题排查与修复
问题背景
在C#中通过RSACryptoServiceProvider实现RSA公钥加密(采用PKCS#1 v1.5填充),加密结果转为逗号分隔的十进制字符串后,C#私钥解密完全正常。但使用Crypto++库实现相同解密逻辑时,抛出RSA/EME-PKCS1-v1_5: invalid ciphertext错误。
C# 加密代码
var rsa = new RSACryptoServiceProvider(); rsa.FromXmlString("xml public string"); var dataToEncrypt = encoder.GetBytes("data to encrypt"); var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray(); // false表示PKCS#1 v1.5填充 var length = encryptedByteArray.Count(); var item = 0; var sb = new StringBuilder(); foreach (var x in encryptedByteArray) { item++; sb.Append(x); if (item < length) sb.Append(","); } return sb.ToString();
加密后输出格式示例:
122,219,42,174,98,8,221,243,154,194,29,118,166......
C# 解密代码
var rsa = new RSACryptoServiceProvider(); var dataArray = data.Split(new char[] { ',' }); byte[] dataByte = new byte[dataArray.Length]; for (int i = 0; i < dataArray.Length; i++) { dataByte[i] = Convert.ToByte(dataArray[i]); } rsa.FromXmlString("xml private key"); var decryptedByte = rsa.Decrypt(dataByte, false); // false表示PKCS#1 v1.5填充 return encoder.GetString(decryptedByte);
C++ 解密代码(存在问题)
std::string RsaDecryption(string cipher, const RSA::PrivateKey& key) { std::stringstream test(cipher); std::string segment; std::vector<std::string> seglist; while (std::getline(test, segment, ',')) { seglist.push_back(segment); } int cipherSegSize = seglist.size(); CryptoPP::byte *cipherByteArray = new CryptoPP::byte[cipherSegSize]; for (int i = 0; i < cipherSegSize; i++) { int tempInt = std::stoi(seglist[i]); cipherByteArray[i] = tempInt; } string recovered; try { AutoSeededRandomPool rng; InvertibleRSAFunction parameters; parameters.GenerateRandomWithKeySize(rng, 1024); RSA::PrivateKey key(parameters); RSAES_PKCS1v15_Decryptor d(key); StringSource ss(cipherByteArray, cipherSegSize, true, new PK_DecryptorFilter(rng, d, new StringSink(recovered) ) // PK_EncryptorFilter ); // StringSource cout << " recovered " << recovered << endl; } catch (CryptoPP::Exception& e) { cerr << "Caught Exception..." << endl; cerr << e.what() << endl; } return recovered; }
C++ 私钥加载代码
// 非真实私钥 string privateKeyPem = R"(-----BEGIN RSA PRIVATE KEY----- MIISQgIBADANBgkqhkiG9w0BAQEFAASCEiwwghIoAgEAAoIEAQDB76wqsKzMcvv9 /aMxbGjn491mscFukj5iMWMHtHYrWFZZUlG5bkVVLQJr6EOIndOTq0qBdLrNog+y NRLetEfXP1Nw5eaJgXeSA9kK4RfG5LBD74buPc2IczyNKsJQdSGIb0c3lRAumU9O dkfDMFOCgNuKsr6lp6hvff/zCCC40lfgJIc++gQ2kev9wDRqMH3u9S0UfNzswlGS wq/kISiKXi2UDL6eOiQ/lpFyWTQ4hH3lqDX5LVkh06cMACUkoN3Sn2EXKCNZZdeB TN9f8biL4WxTY3koUxXS0dOB9+VtjXLvbijmdb0CUYIwTY8DApnUuV== -----END RSA PRIVATE KEY-----)"; RSA::PrivateKey GetPrivateKey() { ByteQueue queue; static string HEADER = "-----BEGIN RSA PRIVATE KEY-----"; static string FOOTER = "-----END RSA PRIVATE KEY-----"; size_t pos1 = privateKeyPem.find(HEADER); if (pos1 == string::npos) throw runtime_error("PEM header not found"); size_t pos2 = privateKeyPem.find(FOOTER, pos1 + 1); if (pos2 == string::npos) throw runtime_error("PEM footer not found"); // 起始位置和长度 pos1 = pos1 + HEADER.length(); pos2 = pos2 - pos1; string keystr = privateKeyPem.substr(pos1, pos2); CryptoPP::StringSource ss{ keystr.c_str(), true }; Base64Decoder decoder; decoder.Attach(new Redirector(queue)); ss.TransferTo(decoder); decoder.MessageEnd(); try { RSA::PrivateKey private_key; if (queue.IsEmpty()) { cerr << "The queue is empty..."; } private_key.BERDecode(queue); AutoSeededRandomPool prng; bool valid = private_key.Validate(prng, 3); if (!valid) cerr << "RSA private key is not valid" << endl; //cout << "N:" << private_key.GetModulus() << endl; //cout << "E:" << private_key.GetPublicExponent() << endl; return private_key; } catch (exception& e) { printf("Caught exception: %s\n", e.what()); exit(1); } }
问题排查与修复
核心错误点
在RsaDecryption函数中,代码重新生成了一个全新的随机RSA私钥,完全覆盖并忽略了传入的合法私钥参数,导致用无效密钥解密合法密文,触发错误:
InvertibleRSAFunction parameters; parameters.GenerateRandomWithKeySize(rng, 1024); RSA::PrivateKey key(parameters); // 此处覆盖了传入的合法私钥
修复后的C++解密代码
std::string RsaDecryption(string cipher, const RSA::PrivateKey& key) { std::stringstream test(cipher); std::string segment; std::vector<std::string> seglist; while (std::getline(test, segment, ',')) { seglist.push_back(segment); } size_t cipherSegSize = seglist.size(); std::vector<CryptoPP::byte> cipherByteArray(cipherSegSize); for (size_t i = 0; i < cipherSegSize; i++) { int tempInt = std::stoi(seglist[i]); cipherByteArray[i] = static_cast<CryptoPP::byte>(tempInt); } string recovered; try { AutoSeededRandomPool rng; RSAES_PKCS1v15_Decryptor d(key); // 使用传入的合法私钥 StringSource ss(cipherByteArray.data(), cipherByteArray.size(), true, new PK_DecryptorFilter(rng, d, new StringSink(recovered) ) ); cout << " recovered " << recovered << endl; } catch (CryptoPP::Exception& e) { cerr << "Caught Exception..." << endl; cerr << e.what() << endl; } return recovered; }
额外优化建议
- 使用
std::vector<CryptoPP::byte>代替手动分配的数组,避免内存泄漏 - 用
size_t代替int处理长度,符合C++类型规范 - 转换字节时添加
static_cast保证类型安全
内容的提问来源于stack exchange,提问作者Abubakar Ikram
相关产品推荐
相关产品推荐

