在Crypto ++中加载PEM编码的私有RSA密钥

时间:2012-03-22 00:50:25

标签: c++ encryption cryptography rsa crypto++

通常,用户将拥有PEM编码的RSA私钥。 Crypto ++要求这些密钥以DER格式加载。我一直在要求人们事先使用openssl手动将他们的PEM文件转换为DER:

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

这很好,但有些人不明白怎么做,也不想。所以我想在程序中自动将PEM文件转换为DER文件。

是否像从PEM中剥离“----- BEGIN CERTIFICATE -----”和“----- END CERTIFICATE -----”一样简单,或者还需要进行其他一些转换?我被告知在这些标记之间它只是b64编码的DER。以下是一些演示此问题的代码:

// 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
}

我想避免对openssl进行系统调用,并完全使用Crypto ++进行转换,以便用户可以提供格式和“正常工作”。谢谢你的任何建议。

3 个答案:

答案 0 :(得分:7)

是的,它是用Base64编码的DER流。但请注意,除了条带化BEGIN和END标记之外,如果是RSA密钥格式,还需要去除可能插入BEGIN标记和编码数据之间的任何标记。只有其余部分可以成功进行Base64解码。您似乎将完整的证书文件提供给解码器,并且需要修复。

答案 1 :(得分:2)

我知道这是一个老问题,但其他人可能觉得这很有用。剥离标记后,您将使用“内部”密钥材料。根据{{​​3}},您可以使用BERDecodePrivateKey加载此内容。因此,要加载一个已剥去其标记的openssl键,您可以执行类似

的操作
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);
}

答案 2 :(得分:2)

  

...我想在程序中自动将PEM文件转换为DER文件。

2014年7月,为Crypto ++库提供了PEM Pack。 PEM Pack是消息加密的部分实现,允许您读取和写入PEM编码的密钥和参数,包括加密的私钥。其他文件包括对RSA,DSA,EC,ECDSA密钥和Diffie-Hellman参数的支持。

它是库的附加组件,而不是库本身的一部分。您下载ZIP并将五个源文件添加到库中。然后你构建库(Crypto ++自动选择它们)。 ZIP包含五个额外的源文件,一个使用OpenSSL创建测试密钥的脚本,一个用于测试读取和写入密钥的C ++程序,以及一个用于验证Crypto ++使用OpenSSL编写的密钥的脚本。

以下是您将如何使用它:

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 ...

如果密钥已加密,那么您可以按照以下方式加载密钥。 PEM Pack重新实现OpenSSL的EVP_BytesToKey,因此密钥派生将起作用,您可以互操作:

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 ...

还有PEM_Save,因此您可以直接从Crypto ++编写密钥。例如:

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

CryptoPP::PEM_Save(file, pk);

PEM_Save用于加密密钥(或您打算加密的密钥):

// 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 需要算法,因为它在封装的标头中编码。 PEM_Save需要一个算法,因为没有默认算法。