从PEM编码的私钥中加载RSA私钥

6

我试图从包含PEM格式私钥的std::string中加载RSA私钥,如下所示:

-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAK8Q+ToR4tWGshaKYRHKJ3ZmMUF6jjwCS/u1A8v1tFbQiVpBlxYB
paNcT2ENEXBGdmWqr8VwSl0NBIKyq4p0rhsCAQMCQHS1+3wL7I5ZzA8G62Exb6RE
INZRtCgBh/0jV91OeDnfQUc07SE6vs31J8m7qw/rxeB3E9h6oGi9IVRebVO+9zsC
IQDWb//KAzrSOo0P0yktnY57UF9Q3Y26rulWI6LqpsxZDwIhAND/cmlg7rUz34Pf
SmM61lJEmMEjKp8RB/xgghzmCeI1AiEAjvVVMVd8jCcItTdwyRO0UjWU4JOz0cnw
5BfB8cSIO18CIQCLVPbw60nOIpUClNxCJzmMLbsrbMcUtgVS6wFomVvsIwIhAK+A
YqT6WwsMW2On5l9di+RPzhDT1QdGyTI5eFNS+GxY
-----END RSA PRIVATE KEY-----

我想知道是否有人能帮助我使用这个密钥,而不是用以下语句生成一个随机数。

CryptoPP::RSA::PrivateKey rsaPrivate; 
rsaPrivate.GenerateRandomWithKeySize (rnd, 512);

私钥是否受密码保护? - jww
不,我从另一个来源获取它并将其放置在这个std :: string中。 - Esteban E
您的问题已添加到Crypto++维基上的Keys and Formats,PEM Encoded Keys页面。 - jww
jww,非常感谢您的帮助,非常感谢,一切都完美解决了! - Esteban E
@jww 再次感谢您 :) - Esteban E
现在,Crypto++提供了一个PEM Pack,其中包含许多PEM读写函数。PEM Pack还与OpenSSL兼容(它实现了OpenSSL的EVP_BytesToKey密钥派生功能)。 - jww
1个回答

10

关键是PEM编码的。您需要去掉PEM头和尾,然后从Base64转换回DER/BER格式,最后使用Crypto++的BERDecodePrivateKey

在Crypto++维基上有一些关于此主题的阅读材料,位于Keys and Formats下。以下是执行转换的代码(我不认为Stack Overflow在Crypto++中有一个有效的示例)。

string RSA_PRIV_KEY =
    "-----BEGIN RSA PRIVATE KEY-----\n"
    "MIIBOgIBAAJBAK8Q+ToR4tWGshaKYRHKJ3ZmMUF6jjwCS/u1A8v1tFbQiVpBlxYB\n"
    "paNcT2ENEXBGdmWqr8VwSl0NBIKyq4p0rhsCAQMCQHS1+3wL7I5ZzA8G62Exb6RE\n"
    "INZRtCgBh/0jV91OeDnfQUc07SE6vs31J8m7qw/rxeB3E9h6oGi9IVRebVO+9zsC\n"
    "IQDWb//KAzrSOo0P0yktnY57UF9Q3Y26rulWI6LqpsxZDwIhAND/cmlg7rUz34Pf\n"
    "SmM61lJEmMEjKp8RB/xgghzmCeI1AiEAjvVVMVd8jCcItTdwyRO0UjWU4JOz0cnw\n"
    "5BfB8cSIO18CIQCLVPbw60nOIpUClNxCJzmMLbsrbMcUtgVS6wFomVvsIwIhAK+A\n"
    "YqT6WwsMW2On5l9di+RPzhDT1QdGyTI5eFNS+GxY\n"
    "-----END RSA PRIVATE KEY-----";

static string HEADER = "-----BEGIN RSA PRIVATE KEY-----";
static string FOOTER = "-----END RSA PRIVATE KEY-----";

size_t pos1, pos2;
pos1 = RSA_PRIV_KEY.find(HEADER);
if(pos1 == string::npos)
    throw runtime_error("PEM header not found");

pos2 = RSA_PRIV_KEY.find(FOOTER, pos1+1);
if(pos2 == string::npos)
    throw runtime_error("PEM footer not found");

// Start position and length
pos1 = pos1 + HEADER.length();
pos2 = pos2 - pos1;
string keystr = RSA_PRIV_KEY.substr(pos1, pos2);

// Base64 decode, place in a ByteQueue  
ByteQueue queue;
Base64Decoder decoder;

decoder.Attach(new Redirector(queue));
decoder.Put((const byte*)keystr.data(), keystr.length());
decoder.MessageEnd();

// Write to file for inspection
FileSink fs("decoded-key.der");
queue.CopyTo(fs);
fs.MessageEnd();

try
{
    CryptoPP::RSA::PrivateKey rsaPrivate;
    rsaPrivate.BERDecodePrivateKey(queue, false /*paramsPresent*/, queue.MaxRetrievable());

    // BERDecodePrivateKey is a void function. Here's the only check
    // we have regarding the DER bytes consumed.
    ASSERT(queue.IsEmpty());
}
catch (const Exception& ex)
{
    cerr << ex.what() << endl;
    exit (1);
}

加载密钥后,您可以使用以下方法验证它:

AutoSeededRandomPool prng;
bool valid = rsaPrivate.Validate(prng, 3);
if(!valid)
    cerr << "RSA private key is not valid" << endl;

并使用以下代码将其打印出来:

cout << "N: " << rsaPrivate.GetModulus() << endl << endl;
cout << "E: " << rsaPrivate.GetPublicExponent() << endl << endl;
cout << "D: " << rsaPrivate.GetPrivateExponent() << endl << endl;

如果密钥受到密码保护,那么 Crypto++ 无法对其进行解码。该库缺乏执行解密的支持。在这种情况下,您可以使用以下 OpenSSL 命令将其转换为 BER/DER 格式。然后,您可以使用密钥材料与 Crypto++ 配合使用。

openssl pkcs8 -nocrypt -in rsa-key.pem -inform PEM -topk8 -outform DER -out rsa-key.der

使用以下代码将密钥写入文件:

FileSink fs("decoded-key.der");
queue.CopyTo(fs);
fs.MessageEnd();

CopyTo 方法将字节留在队列中以备后用。 您可以使用 ASN.1 工具(如 Gutmann 的 dumpasn1)转储文件:

CopyTo留下的字节将在稍后使用时保留在queue中。您可以使用ASN.1工具(例如Gutmann的dumpasn1)转储文件:

$ dumpasn1 decoded-key.der 
  0 314: SEQUENCE {
  4   1:   INTEGER 0
  7  65:   INTEGER
       :     00 AF 10 F9 3A 11 E2 D5 86 B2 16 8A 61 11 CA 27
       :     76 66 31 41 7A 8E 3C 02 4B FB B5 03 CB F5 B4 56
       :     D0 89 5A 41 97 16 01 A5 A3 5C 4F 61 0D 11 70 46
       :     76 65 AA AF C5 70 4A 5D 0D 04 82 B2 AB 8A 74 AE
       :     1B
 74   1:   INTEGER 3
 77  64:   INTEGER
       :     74 B5 FB 7C 0B EC 8E 59 CC 0F 06 EB 61 31 6F A4
       :     44 20 D6 51 B4 28 01 87 FD 23 57 DD 4E 78 39 DF
       :     41 47 34 ED 21 3A BE CD F5 27 C9 BB AB 0F EB C5
       :     E0 77 13 D8 7A A0 68 BD 21 54 5E 6D 53 BE F7 3B
143  33:   INTEGER
       :     00 D6 6F FF CA 03 3A D2 3A 8D 0F D3 29 2D 9D 8E
       :     7B 50 5F 50 DD 8D BA AE E9 56 23 A2 EA A6 CC 59
       :     0F
178  33:   INTEGER
       :     00 D0 FF 72 69 60 EE B5 33 DF 83 DF 4A 63 3A D6
       :     52 44 98 C1 23 2A 9F 11 07 FC 60 82 1C E6 09 E2
       :     35
213  33:   INTEGER
       :     00 8E F5 55 31 57 7C 8C 27 08 B5 37 70 C9 13 B4
       :     52 35 94 E0 93 B3 D1 C9 F0 E4 17 C1 F1 C4 88 3B
       :     5F
248  33:   INTEGER
       :     00 8B 54 F6 F0 EB 49 CE 22 95 02 94 DC 42 27 39
       :     8C 2D BB 2B 6C C7 14 B6 05 52 EB 01 68 99 5B EC
       :     23
283  33:   INTEGER
       :     00 AF 80 62 A4 FA 5B 0B 0C 5B 63 A7 E6 5F 5D 8B
       :     E4 4F CE 10 D3 D5 07 46 C9 32 39 78 53 52 F8 6C
       :     58
       :   }

0 warnings, 0 errors.

jww,我感激你的帮助,非常谢谢,它运行得正确。 - Esteban E
谢谢@EstebanE。您可能还想看一下“接受答案的工作原理是什么?”(https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work)。目前来看,您的问题尚未得到正式回答。 - jww
谢谢你的回复,但是由于我的声望不够,我无法为你的回复投票。 - Esteban E

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