You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
}

额外优化建议

  1. 使用std::vector<CryptoPP::byte>代替手动分配的数组,避免内存泄漏
  2. 用size_t代替int处理长度,符合C++类型规范
  3. 转换字节时添加static_cast保证类型安全

内容的提问来源于stack exchange,提问作者Abubakar Ikram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:01:14