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

时间:2019-07-26 09:53:21

标签: c++ openssl public-key pem jwk

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相同的PEM在线工具提供了什么?

2 个答案:

答案 0 :(得分:1)

您已经非常接近第一个实现中的解决方案,只是在最后一步,您应该使用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的引号

  

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

     

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

并且来自RFC 3280

  

4.1.2.7主题公钥信息

     

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

答案 1 :(得分:0)

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