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

How to convert JWK public key to PEM format in C++

Here 是一个在线工具,可以将 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 作为在线工具提供?

这是我使用 CryptoPP and 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();
}

您在第一次实施时已经非常接近解决方案,只是在最后一步,而不是 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);

更多信息,请参考this discussion

的引述

The RSAPublicKey functions process an RSA public key using an RSA structure. The public key is encoded using a PKCS#1 RSAPublicKey structure.

The RSA_PUBKEY functions also process an RSA public key using an RSA structure. However the public key is encoded using a SubjectPublicKeyInfo structure and an error occurs if the public key is not RSA .

RFC 3280,

4.1.2.7 Subject Public Key Info

This field is used to carry the public key and identify the algorithm with which the key is used (e.g., RSA, DSA, or Diffie-Hellman). The algorithm is identified using the AlgorithmIdentifier structure specified in section 4.1.1.2. The object identifiers for the supported algorithms and the methods for encoding the public key materials (public key and parameters) are specified in [PKIXALGS].