首页 > 解决方案 > 如何在 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-----

反之亦然。因此,ktyandkid字段也以某种方式包含在 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++ lib 实现正确的转换,因此它还将考虑kidkty字段并使用与在线工具提供的相同 PEM 的结果?

标签: c++opensslpublic-keypemjwk

解决方案


您在第一个实现中已经非常接近解决方案,只是在最后一步,而不是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 节中指定的 AlgorithmIdentifier 结构来标识。[PKIXALGS] 中规定了支持算法的对象标识符和对公钥材料(公钥和参数)进行编码的方法。


推荐阅读