如何在C++中将 JWK 公钥转换为 PEM 格式



这是一个在线工具,可以将JWK转换为PEM,反之亦然。

我希望C++代码中也是如此。

对于 JWK:

{  
"kty":"RSA",
"e":"AQAB",
"kid":"18b4f6a6-f9ec-456b-a3e8-04af5e97790e",
"n":"tVKUtcx_n9rt5afY_2WFNvU6PlFMggCatsZ3l4RjKxH0jgdLq6CScb0P3ZGXYbPzXvmmLiWZizpb-h0qup5jznOvOr-Dhw9908584BSgC83YacjWNqEK3urxhyE2jWjwRm2N95WGgb5mzE5XmZIvkvyXnn7X8dvgFPF5QwIngGsDG8LyHuJWlaDhr_EPLMW4wHvH0zZCuRMARIJmmqiMy3VD4ftq4nS5s8vJL0pVSrkuNojtokp84AtkADCDU_BUhrc2sIgfnvZ03koCQRoZmWiHu86SuJZYkDFstVTVSR0hiXudFlfQ2rOhPlpObmku68lXw-7V-P7jwrQRFfQVXw"
}

在线工具为 PEM 提供:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVKUtcx/n9rt5afY/2WF
NvU6PlFMggCatsZ3l4RjKxH0jgdLq6CScb0P3ZGXYbPzXvmmLiWZizpb+h0qup5j
znOvOr+Dhw9908584BSgC83YacjWNqEK3urxhyE2jWjwRm2N95WGgb5mzE5XmZIv
kvyXnn7X8dvgFPF5QwIngGsDG8LyHuJWlaDhr/EPLMW4wHvH0zZCuRMARIJmmqiM
y3VD4ftq4nS5s8vJL0pVSrkuNojtokp84AtkADCDU/BUhrc2sIgfnvZ03koCQRoZ
mWiHu86SuJZYkDFstVTVSR0hiXudFlfQ2rOhPlpObmku68lXw+7V+P7jwrQRFfQV
XwIDAQAB
-----END PUBLIC KEY-----

反之亦然。因此,ktykid字段也以某种方式包含在 PEM 中。

我已经尝试过这样的OpenSSL:

std::string_view nnInBase64Url = "tVKUtcx_n9rt5afY_2WFNvU6PlFMggCatsZ3l4RjKxH0jgdLq6CScb0P3ZGXYbPzXvmmLiWZizpb-h0qup5jznOvOr-Dhw9908584BSgC83YacjWNqEK3urxhyE2jWjwRm2N95WGgb5mzE5XmZIvkvyXnn7X8dvgFPF5QwIngGsDG8LyHuJWlaDhr_EPLMW4wHvH0zZCuRMARIJmmqiMy3VD4ftq4nS5s8vJL0pVSrkuNojtokp84AtkADCDU_BUhrc2sIgfnvZ03koCQRoZmWiHu86SuJZYkDFstVTVSR0hiXudFlfQ2rOhPlpObmku68lXw-7V-P7jwrQRFfQVXw";
std::string_view eeInBase64Url = "AQAB";
auto nnBin = cppcodec::base64_url_unpadded::decode(nnInBase64Url);
auto eeBin = cppcodec::base64_url_unpadded::decode(eeInBase64Url);
BIGNUM* modul = BN_bin2bn(nnBin.data(),nnBin.size(),NULL);
BIGNUM* expon = BN_bin2bn(eeBin.data(),eeBin.size(),NULL);
RSA* rr = RSA_new();
RSA_set0_key(rr, modul, expon, NULL);
BIO* ff = BIO_new_file("public.pem","w+");
PEM_write_bio_RSAPublicKey(ff, rr);

但它给了我一个不同的 PEM,这应该是显而易见的,因为至少我没有指定kid.

最后,问题是:如何使用OpenSSL或其他C++库实现正确的转换,因此它还将考虑kidkty字段以及与在线工具相同的PEM结果?

您在第一个实现中已经非常接近解决方案,只是在最后一步,而不是PEM_write_bio_RSAPublicKey您应该使用PEM_write_bio_RSA_PUBKEY.

std::string_view nnInBase64Url = "tVKUtcx_n9rt5afY_2WFNvU6PlFMggCatsZ3l4RjKxH0jgdLq6CScb0P3ZGXYbPzXvmmLiWZizpb-h0qup5jznOvOr-Dhw9908584BSgC83YacjWNqEK3urxhyE2jWjwRm2N95WGgb5mzE5XmZIvkvyXnn7X8dvgFPF5QwIngGsDG8LyHuJWlaDhr_EPLMW4wHvH0zZCuRMARIJmmqiMy3VD4ftq4nS5s8vJL0pVSrkuNojtokp84AtkADCDU_BUhrc2sIgfnvZ03koCQRoZmWiHu86SuJZYkDFstVTVSR0hiXudFlfQ2rOhPlpObmku68lXw-7V-P7jwrQRFfQVXw";
std::string_view eeInBase64Url = "AQAB";
auto nnBin = cppcodec::base64_url_unpadded::decode(nnInBase64Url);
auto eeBin = cppcodec::base64_url_unpadded::decode(eeInBase64Url);
BIGNUM* modul = BN_bin2bn(nnBin.data(),nnBin.size(),NULL);
BIGNUM* expon = BN_bin2bn(eeBin.data(),eeBin.size(),NULL);
RSA* rr = RSA_new();
RSA_set0_key(rr, modul, expon, NULL);
BIO* ff = BIO_new_file("public.pem","w+");
PEM_write_bio_RSA_PUBKEY(ff, rr);

有关更多信息,请参阅此讨论的引文

RSAPublicKey 函数使用 RSA 结构处理 RSA 公钥。公钥使用 PKCS#1 RSAPublicKey 结构进行编码。

RSA_PUBKEY函数还使用 RSA 结构处理 RSA 公钥。但是,公钥是使用 SubjectPublicKeyInfo 结构编码的,如果公钥不是 RSA,则会发生错误。

以及来自 RFC 3280,

4.1.2.7 主题公钥信息

此字段用于携带公钥和标识算法 使用密钥的内容(例如,RSA、DSA 或 Diffie-Hellman)。 这 算法使用算法标识符结构进行标识 在第 4.1.1.2 节中指定。 的对象标识符 支持的算法和公钥编码方法 材料(公钥和参数)在 [PKIXALGS] 中指定。

以下是我使用CryptoPP和CryptoPP-PEM找到的解决方案:

std::string getRSAPublicKeyInPEMFormat(std::string_view nnInBase64UrlUnpadded, std::string_view eeInBase64UrlUnpadded)
{
auto nnBin = cppcodec::base64_url_unpadded::decode(nnInBase64UrlUnpadded);
auto eeBin = cppcodec::base64_url_unpadded::decode(eeInBase64UrlUnpadded);
CryptoPP::Integer nn(nnBin.data(), nnBin.size(), CryptoPP::Integer::UNSIGNED, CryptoPP::BIG_ENDIAN_ORDER);
CryptoPP::Integer ee(eeBin.data(), eeBin.size(), CryptoPP::Integer::UNSIGNED, CryptoPP::BIG_ENDIAN_ORDER);
CryptoPP::RSA::PublicKey pubKey;
pubKey.Initialize(nn, ee);
std::ostringstream pem;
CryptoPP::FileSink sink(pem);
CryptoPP::PEM_Save(sink, pubKey);
return pem.str();
}

最新更新