Load PEM encoded private RSA key in Crypto++

2019-02-06 08:55发布

Often times, user will have PEM encoded RSA private keys. Crypto++ requires that these keys be in DER format to load. I've been asking people to manually convert their PEM files to DER beforehand using openssl like this:

openssl pkcs8 -in in_file.pem -out out_file.der -topk8 -nocrypt -outform der

That works fine, but some people don't understand how to do that nor do they want to. So I would like to convert PEM files to DER files automatically within the program.

Is it as simple as striping the "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" from the PEM or is some other transformation required as well? I've been told that between those markers that it's just b64 encoded DER. Here's some code that demonstrates the issue:

// load the private key
CryptoPP::RSA::PrivateKey PK;
CryptoPP::ByteQueue bytes;

try
{
    CryptoPP::FileSource File( rsa.c_str(), true, new CryptoPP::Base64Decoder() );
    File.TransferTo( bytes );
    bytes.MessageEnd();

    // This line Causes BERDecodeError when a PEM encoded file is used
    PK.Load( bytes );
}

catch ( CryptoPP::BERDecodeErr )
{
    // Convert PEM to DER and try to load the key again
}

I'd like to avoid making system calls to openssl and do the transformation entirely in Crypto++ so that users can provide either format and things "just work". Thanks for any advice.

3条回答
太酷不给撩
2楼-- · 2019-02-06 09:08

I know this is an old question but other's might find this useful. Once you strip the markers you're left with the 'inner' key material. According to http://www.cryptopp.com/wiki/Keys_and_Formats#BER_and_DER_Encoding you can use BERDecodePrivateKey to load this. So, to load an openssl key that has had its markers stripped you can do something like

bool LoadKey(RandomNumberGenerator& rng, const std::string& file, 
    RSA::PrivateKey& key)
{
    ByteQueue q;
    FileSource KeyFile(file.c_str(), true, new Base64Decoder);
    KeyFile.TransferTo(q);
    key.BERDecodePrivateKey(q,false,0); // last 2 params unused
    return key.Validate(rng, 2);
}
查看更多
对你真心纯属浪费
3楼-- · 2019-02-06 09:20

Yes, it's a DER stream encoded with Base64. Note though, in addition to striping both BEGIN and END markers, in case of RSA key format you also need to strip any flags that may be inserted between the BEGIN marker and the encoded data. Only the remaining part can be successfully Base64 decoded. It appears that you feed the full certificate file to the decoder and that needs fixing.

查看更多
冷血范
4楼-- · 2019-02-06 09:27

... I would like to convert PEM files to DER files automatically within the program.

In July 2014, a PEM Pack was provided for the Crypto++ library. The PEM Pack is a partial implementation of message encryption which allows you to read and write PEM encoded keys and parameters, including encrypted private keys. The additional files include support for RSA, DSA, EC, ECDSA keys and Diffie-Hellman parameters.

Its an add-on to the library, and not part of the library proper. You download a ZIP and add five source files to the library. Then you build the library (Crypto++ automatically picks them up). The ZIP contains five additional source files, a script to create test keys using OpenSSL, a C++ program to test reading and writing the keys, and a script to verify the keys written by Crypto++ using OpenSSL.

Here's how you would use it:

CryptoPP::RSA::PrivateKey pk;
CryptoPP::FileSource file("<rsa-key-file.pem>", true);

CryptoPP::PEM_Load(file, pk);

CryptoPP::AutoSeededRandomPool prng;
bool = pk.Validate(prng, 3);
if (! valid)
    throw ...

If the keys is encrypted, then here's how you would load it. The PEM Pack re-implement's OpenSSL's EVP_BytesToKey, so the key derivation will work and you can interop:

CryptoPP::RSA::PrivateKey pk;
CryptoPP::FileSource file("<rsa-key-file.pem>", true);

std::string pass = "<super secret password>";
CryptoPP::PEM_Load(file, pk, pass.data(), pass.size());

CryptoPP::AutoSeededRandomPool prng;
bool = pk.Validate(prng, 3);
if (! valid)
    throw ...

There's also a PEM_Save, so you can write the keys directly from Crypto++. For example:

// Generate it or load it from somewhere
CryptoPP::RSA::PrivateKey pk = ...; 
CryptoPP::FileSink file("<rsa-key-file.pem>", true);

CryptoPP::PEM_Save(file, pk);

And PEM_Save for an encrypted key (or key you intend to encrypt):

// Generate it or load it from somewhere
CryptoPP::RSA::PrivateKey pk = ...; 
CryptoPP::FileSink file("<rsa-key-file.pem>", true);

std::string pass = "<super secret password>";
CryptoPP::PEM_Save(file, pk, "AES-128-CBC", pass.data(), pass.size());

PEM_Load does not need an algorithm because its encoded in the encapsulated header. PEM_Save needs an algorithm because there is no default algorithm.

查看更多
登录 后发表回答