How would I load a Private / Public Key from a string / byte array or any other container

deW1 picture deW1 · Mar 14, 2015 · Viewed 11.3k times · Source

Is it possible to store a RSA Private/Public Key in the source for example in a byte[] or string or any other container and use this key for encryption / decryption?

A decode function from file would look like:

void Decode(const string& filename, BufferedTransformation& bt)
{
    // http://www.cryptopp.com/docs/ref/class_file_source.html
    FileSource file(filename.c_str(), true /*pumpAll*/);

    file.TransferTo(bt);
    bt.MessageEnd();
}

That loads the key from file which is not what I want.

I know it's gotta be possible since I can create the key with AutoSeededRandomPool.

I just don't know how to use an existing one.

Maybe I'm overlooking this part in the documentation.

Answer

softwariness picture softwariness · Mar 14, 2015

The Crypto++ Keys and Formats and Crypto++ RSA Cryptography pages may be of interest.

If you're generating the RSA parameters like this:

AutoSeededRandomPool rng;

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

You can use the use the DEREncode and BERDecode methods of InvertibleRSAFunction to encode and decode all the parameters respectively:

{
    FileSink output("rsaparams.dat");
    params.DEREncode(output);
}

InvertibleRSAFunction params2;
{
    FileSource input("rsaparams.dat", true);
    params2.BERDecode(input);
}

To encode/decode the private and public material separately, use the DEREncode and BERDecode methods on the RSA::PrivateKey and RSA::PublicKey objects themselves:

// Initialize keys from generated params
RSA::PrivateKey rsaPrivate(params);
RSA::PublicKey rsaPublic(params);

// Write keys to file
{
    FileSink output("rsaprivate.dat");
    rsaPrivate.DEREncode(output);
}
{
    FileSink output("rsapublic.dat");
    rsaPublic.DEREncode(output);
}

// Read keys from file into new objects
RSA::PrivateKey rsaPrivate2;
RSA::PublicKey rsaPublic2;
{
    FileSource input("rsaprivate.dat", true);
    rsaPrivate2.BERDecode(input);
}
{
    FileSource input("rsapublic.dat", true);
    rsaPublic2.BERDecode(input);
}

FileSource and FileSink are just example source and sink objects you could use. The encode/decode routines take BufferedTransformation objects as parameters, so you can use any other suitable implementations of that interface.

For example, ArraySink can be used to write data into a memory buffer you supply, and StringSource (also aliased as ArraySource) can be used to read from a buffer.

Here's some code showing use of ArraySink and ArraySource to round-trip the private key material through a std::vector<byte>:

RSA::PrivateKey rsaPrivate(params);
std::vector<byte> buffer(8192 /* buffer size */);

ArraySink arraySink(&buffer[0], buffer.size());
rsaPrivate.DEREncode(arraySink);

// Initialize variable with the encoded key material
// (excluding unwritten bytes at the end of our buffer object)
std::vector<byte> rsaPrivateMaterial(
    &buffer[0],
    &buffer[0] + arraySink.TotalPutLength());

RSA::PrivateKey rsaPrivate2;
ArraySource arraySource(
    &rsaPrivateMaterial[0],
    rsaPrivateMaterial.size(),
    true);
rsaPrivate2.BERDecode(arraySource);

(See also @jww's answer for an example which avoids the fixed-size buffer by using a ByteQueue).

And another example using a std::string to store the key material and using the StringSink class to write to this, which avoids some of the buffer management (the string will be resized automatically to match the amount of data that is encoded). Note that this is still binary data even though it's in a std::string object.

RSA::PrivateKey rsaPrivate(params);

std::string rsaPrivateMaterial;
StringSink stringSink(rsaPrivateMaterial);
rsaPrivate.DEREncode(stringSink);

RSA::PrivateKey rsaPrivate2;
StringSource stringSource(rsaPrivateMaterial, true);
rsaPrivate2.BERDecode(stringSource);

Alternatively, if you want to control the format yourself, you can use the methods of the InvertibleRSAFunction object or the key objects to extract the individual parameters (as shown in the "Crypto++ RSA Cryptography" link above) and use that to extract the values for storage in your own format:

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();

These could then be restored to a new InvertibleRSAFunction or RSA::*Key instance when read from the file or container, by using the corresponding setter methods (SetModulus(), SetPrime1(), etc.).