Search code examples
c++rsapublic-key-encryptioncrypto++

How to initialize RSA::PrivateKey?


I'm trying to use Crypto++'s RSA encryption. The problem is how to initialize RSA::PrivateKey from number string?

The code to generate key pairs(from here)

cout << hex ;

AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 2048);

params.SetPublicExponent(65537);

const Integer& n = params.GetModulus();
const Integer& p = params.GetPrime1();
const Integer& q = params.GetPrime2();
const Integer& d = params.GetPrivateExponent();
const Integer& e = params.GetPublicExponent();

///////////////////////////////////////
// Dump
cout << "RSA Parameters:" << endl;
cout << " n: " << n << endl;
cout << " p: " << p << endl;
cout << " q: " << q << endl;
cout << " d: " << d << endl;
cout << " e: " << e << endl;
cout << endl;

So I get n,d,e string, should be able to initialize the private key, then I found some sample code here:

Integer n("0xbeaadb3d839f3b5f"), e("0x11"), d("0x21a5ae37b9959db9");

RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);

RSA::PublicKey pubKey;
pubKey.Initialize(n, e);

The code works, it doesn't throw any exception. So I try to change the n,e,d to the string generated previously.

// n: b0f2bee69386528216049775704d402cb3ff443ca2ea25a74b11c1c9c321b7ea46327b4e413f532616812fece07d061cf96e373789b3b9b05d2d31174c700a066868d26c52b5d48e6dbaf664fac66ee31747133a6569e16d12f521b56a12aadd74e7cf2534353a5e338173b8f884a568a25173f3a33782f9047af59da9b21180534923e5210c3989851f0d69d68d92c272769fbf2a833e2f522f60f76bec12d3b194c2f3b945c913649e2be54295a2f58e7c040bf61421f01077fdf234ddfe73663deec8979256c721fd65c046a7d21530adec1af2922ed6a27004bf31a04cd57981ca22208572743b6b64d4d30b0efe446fc7608b4178ff8a0ba7db3e45ecf3h
// e: 10001h
// d: 246e365ca5e6f2de8c100110a62e05aed9c39d1b8af3f8b1806589c7a82c96ce59bf1962ef50cd5aaa47c61a2e37db9c8db4cf2205c31eb35e7a3ed017443e4c9d0685ace3da243b70f1c951067425e375bbcf40ba86bd7856b9ff691d5e323ca720aaa5c6fbe65eb0404c87f6ee220e034d0148bfb89af70873ab09df2c30c74104b0973aa4e93ca95db749da4f6b2d9594ab487db1f6f194ab0b77bd91d834daf269c63d3abecad54a1a71599524e679a425c55b16a9ff7f0c37b2d259eb44ea5782f314f61cc0ac874b2e6ae870d798e90e5bc96ab57c8fd904fa9d199c46c971de3a5d7cabfdca0663373843bd41ec246e158754dabc9ec2172f7a5982edh

RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);

It crashes. I googled a while and found some other tip:

InvertibleRSAFunction params;
params.Initialize(n, e, d);

RSA::PrivateKey(params);

But it still crashes. What's the correct way of initializing a 2048-bit rsa private key?


Solution

  •     // n: b0f2bee69386528216049775704d402cb3ff443ca2ea25a74b11c1c9c321b7ea46327b4e413f532616812fece07d061cf96e373789b3b9b05d2d31174c700a066868d26c52b5d48e6dbaf664fac66ee31747133a6569e16d12f521b56a12aadd74e7cf2534353a5e338173b8f884a568a25173f3a33782f9047af59da9b21180534923e5210c3989851f0d69d68d92c272769fbf2a833e2f522f60f76bec12d3b194c2f3b945c913649e2be54295a2f58e7c040bf61421f01077fdf234ddfe73663deec8979256c721fd65c046a7d21530adec1af2922ed6a27004bf31a04cd57981ca22208572743b6b64d4d30b0efe446fc7608b4178ff8a0ba7db3e45ecf3h
        // e: 10001h
        // d: 246e365ca5e6f2de8c100110a62e05aed9c39d1b8af3f8b1806589c7a82c96ce59bf1962ef50cd5aaa47c61a2e37db9c8db4cf2205c31eb35e7a3ed017443e4c9d0685ace3da243b70f1c951067425e375bbcf40ba86bd7856b9ff691d5e323ca720aaa5c6fbe65eb0404c87f6ee220e034d0148bfb89af70873ab09df2c30c74104b0973aa4e93ca95db749da4f6b2d9594ab487db1f6f194ab0b77bd91d834daf269c63d3abecad54a1a71599524e679a425c55b16a9ff7f0c37b2d259eb44ea5782f314f61cc0ac874b2e6ae870d798e90e5bc96ab57c8fd904fa9d199c46c971de3a5d7cabfdca0663373843bd41ec246e158754dabc9ec2172f7a5982edh
        RSA::PrivateKey privKey;
        privKey.Initialize(n, e, d);
    

    It crashes.

    We need to see the actual code. I'm guessing two things. First (1), you are not using a try/catch, so the program terminates due to the uncaught exception. To fix this:

    try
    {
        // Some operation
    }
    catch (const Exception& ex)
    {
        cerr << ex.what() << endl;
    }
    

    Second (2), you are using strings rather than Integers in the call to Iniaitialize. To fix this:

    string n = "b0f2bee693865282...8a0ba7db3e45ecf3h";
    string e = "10001h";
    string d = "246e365ca5e6f2de...9ec2172f7a5982edh";
    
    Integer _n(n.c_str()), _e(e.c_str()), _d(d.c_str());
    RSA::PrivateKey privKey;
    privKey.Initialize(_n, _e, _d);
    

    The second issue should have been caught by the compiler. You should consider using -Wall to get a basic set of compiler diagnostics.


    There's a potential third issue. That's the "key fails to validate" case. If you add a try/catch as described in (1), then you may see an "Invalid key material" or similar from CryptoMaterial class. In this case, your parameters do not validate according to the checks performed by Validate.

    Validate is called by Initialize using a low-level of thoroughness (the level parameter below). The Validate function can be found in the manual at InvertibleRSAFunction::Validate. Following the link Definition at line 247 of file rsa.cpp:

      247 bool InvertibleRSAFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const
      248 {
      249     bool pass = RSAFunction::Validate(rng, level);
      250     pass = pass && m_p > Integer::One() && m_p.IsOdd() && m_p < m_n;
      251     pass = pass && m_q > Integer::One() && m_q.IsOdd() && m_q < m_n;
      252     pass = pass && m_d > Integer::One() && m_d.IsOdd() && m_d < m_n;
      253     pass = pass && m_dp > Integer::One() && m_dp.IsOdd() && m_dp < m_p;
      254     pass = pass && m_dq > Integer::One() && m_dq.IsOdd() && m_dq < m_q;
      255     pass = pass && m_u.IsPositive() && m_u < m_p;
      256     if (level >= 1)
      257     {
      258         pass = pass && m_p * m_q == m_n;
      259         pass = pass && m_e*m_d % LCM(m_p-1, m_q-1) == 1;
      260         pass = pass && m_dp == m_d%(m_p-1) && m_dq == m_d%(m_q-1);
      261         pass = pass && m_u * m_q % m_p == 1;
      262     }
      263     if (level >= 2)
      264         pass = pass && VerifyPrime(rng, m_p, level-2) && VerifyPrime(rng, m_q, level-2);
      265     return pass;
      266 }
    

    The remaining open question might be, where did m_p, m_q, etc come from? The answer is Initialize factors n based on e and d, and populates the CRT values like p, q, dp, dq, etc. It speeds up computation later.


    This may be related.... e=10001h tells me Crypto++ probably did not generate the key pair. If Crypto++ generated the key pair, then it would use e=17 by default. How did you generate the key pair?