使用Crypto++加载PEM编码的RSA私钥

12

通常情况下,用户会拥有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-----"标识,还是需要进行其他的转换吗?有人告诉我,在这些标识之间只是base64编码的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++中完成转换,以便用户可以提供任何格式并使其“正常工作”。感谢任何建议。


你是在寻找内存解码器,还是磁盘上的解码器也可以? - std''OrgnlDave
@OrgnlDave - 任何一个使用 Crypto++ 的例子都可以。 - 01100110
@01100110 - Crypto++现在已经支持此功能作为附加组件。请参阅Crypto++维基上的PEM Pack - jww
3个回答

9

是的,这是一个Base64编码的DER流。需要注意的是,在RSA密钥格式的情况下,除了删除BEGIN和END标记之外,还需要删除在BEGIN标记和编码数据之间可能插入的任何标志。只有剩余部分才能成功进行Base64解码。看起来你把完整的证书文件提供给了解码器,需要修复这个问题。


感谢您的回答。我可以使用 std::system 调用 openssl 进行转换。这样可以正常工作,但它只是一个 hack。我想完全在 Crypto++ 中进行 PEM 到 DER 的转换。 - 01100110
1
我已经开始了一项悬赏。如果能够完全使用Crypto++和标准C++来实现,那么我将授予任何能够提供可行源代码示例的人这个悬赏奖励。 - 01100110
1
你已经得到了正确的答案,应该自己编写代码。Base 64和一些字符串处理不应该太难。 - Maarten Bodewes
1
@owlstead - 我可以做这些事情。关于在BEGIN标记后剥离RSA标志的部分对我来说不是很清楚。我经常使用Crypto++编码,所以我可以处理它,但我想看到的是一个具体的例子。我在其他任何地方都没有找到相关文档。希望这里有人已经做过。我还应该补充一点,我不生成这些PEM文件,也无法控制或输入它们的生成方式。 - 01100110
2
问题在于你要求我们为你创建代码,但这不是本网站的目的。而且你在提出一个不需要生成任何代码的问题后才这样做,这使得 orLcount 无法得到答案。 - Maarten Bodewes
显示剩余2条评论

4

我希望程序内部能自动将PEM文件转换为DER文件。

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

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

以下是如何使用它的方法:

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的,因此密钥派生将起作用,您可以进行互操作:
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());
< p > < code > PEM_Load 不需要算法,因为它已经编码在封装头中。 < code > PEM_Save 需要算法,因为没有默认算法。


3

我知道这是一个旧问题,但其他人可能会发现这很有用。一旦你去掉标记,你就会得到'inner'关键材料。根据http://www.cryptopp.com/wiki/Keys_and_Formats#BER_and_DER_Encoding,你可以使用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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接