如何将 JWK 转换为 PEM

Gen*_*erd 6 cryptography rsa pseudocode pem jwk

这个问题更通用,而不是针对特定语言,因此我将解释我的问题以及我在伪代码中尝试过的内容。

我正在尝试从 JWK 集中生成 PEM 公钥。JWK 包括“e”(指数)和“n”(模)变量。我的问题是,在不使用任何库和 OpenSSL 命令行工具的情况下将此 JWK 转换为 PEM 的确切步骤是什么。

这是JWK供参考:

{
  "kty": "RSA",
  "alg": "RS512",
  "kid": "26887d3ee3293c526c0e6dd05f122df53aa3f13d7dad06d25e266fa6f51db79fb52422aaf79f121476237e98dcd6640350fee47fec70e783544ec9a36e4605bc",
  "use": "sig",
  "n": "14m79mVwIE0JxQdKrgXVf7dVcBS90U0TvG7Yf7dG4NJocz1PNUrKrzGhe_FryOe0JahL_sjA2_rKw7NBCpuVx_zSPFRw6kqjewGicjXGus5Fmlf3zDuqwV4BWIFHyQexMPOly0agFfcM0M0MgBULXjINgBs9MwnRv7JVfRoGqXHsNM45djFDd3o4liu4LPlge_DquZUFLNu-BYAyAlWkz0H2TepZhGrN9VEPmxzQkNzXc1R4MpZvbxrRRgaAA2z094ik3hk86JhfyFq-LDcueZhtshmrYZ95LWgMlQ7PixkeK1HkeEYMt20lmNzR8B8KabimYmibxA4Ay9gpRwfp-Q",
  "e": "AQAB"
}
Run Code Online (Sandbox Code Playgroud)

我的大部分研究源自node-jwk-to-pem库(可以在这里找到: https: //github.com/Brightspace/node-jwk-to-pem)和一个使用JOSE库的StackOverflow问题在 PHP 中(可以在这里找到:How to conversion a public key from a JWK into PEM for OpenSSL?

根据我通过阅读上述库(加上一些其他文章和问题,但没有完全提到逐步过程)所发现的,我发现第一步是将模数和指数转换为整数(特别是 BigInt)。这通常会导致以下结果:

n = 27209154847141595039206198313134388207882403216639061643382959557246344691110642716248339592972939239825436848454712213431917464875221502188500634554426063986720722251339335488215426908010590565758292169712942346285217861588132983738839891250753593240057639347814778952277719613395670962156961452389927728643840215833830614315091417876959205843957512422401240879135352731575182574836052718961865690645602829768565458494497550672252951063585023601307115444743487394113997186698238507983094748342588645472362960665610355698438390751920697759620235642103374737421940385132232531739910444003185620313592808726865629407737
e = 65537
Run Code Online (Sandbox Code Playgroud)

使用此信息,我将如何从指数和模数生成 PEM 公钥?任何伪代码或建议都将是令人难以置信的!

jcc*_*ero 7

RSA公钥n由模数和指数定义e

您可以使用和字段直接从JWK 类型RSA获取该信息:ne

“n”(模数)参数包含 RSA 公钥的模值。它表示为 Base64urlUInt 编码值。

“e”(指数)参数包含 RSA 公钥的指数值。它表示为 Base64urlUInt 编码值。

要创建表示此公钥信息的 PEM 文件,您基本上需要构建其等效的 ASN.1 DER 编码表示形式,可能还需要一些前导码。在其原始形式中,它在RFC3447中是这样定义的:

RSAPublicKey ::= SEQUENCE {
  modulus           INTEGER,  -- n
  publicExponent    INTEGER   -- e
}
Run Code Online (Sandbox Code Playgroud)

ASN.1定义用于表示任意数据结构的规则和类型,并且为此目的在密码学中广泛使用。

ASN.1 仅提供语法,但该信息的实际编码是使用不同的编码格式(尤其是 DER)执行的。

执行编码的方式并不简单:Let's Encrypt 提供了一篇关于该主题的优秀文章。出色的lapo.it/asn1js 站点还提供了一个用于检查 ASN.1 DER 编码信息的出色工具。

例如,考虑从下面提到的 SO 问题中提取的以下模数和指数:

SEQUENCE (2 elements)
   INTEGER (2048 bit): EB506399F5C612F5A67A09C1192B92FAB53DB28520D859CE0EF6B7D83D40AA1C1DCE2C0720D15A0F531595CAD81BA5D129F91CC6769719F1435872C4BCD0521150A0263B470066489B918BFCA03CE8A0E9FC2C0314C4B096EA30717C03C28CA29E678E63D78ACA1E9A63BDB1261EE7A0B041AB53746D68B57B68BEF37B71382838C95DA8557841A3CA58109F0B4F77A5E929B1A25DC2D6814C55DC0F81CD2F4E5DB95EE70C706FC02C4FCA358EA9A82D8043A47611195580F89458E3DAB5592DEFE06CDE1E516A6C61ED78C13977AE9660A9192CA75CD72967FD3AFAFA1F1A2FF6325A5064D847028F1E6B2329E8572F36E708A549DDA355FC74A32FDD8DBA65
   INTEGER (24 bit): 010001
Run Code Online (Sandbox Code Playgroud)

对应的ASN.1 DER编码如下:

30 82 01 0A      ;sequence (0x10A bytes long)
   02 82 01 01   ;integer (0x101 bytes long)
      00 EB506399F5C612F5A67A09C1192B92FAB53DB28520D859CE0EF6B7D83D40AA1C1DCE2C0720D15A0F531595CAD81BA5D129F91CC6769719F1435872C4BCD0521150A0263B470066489B918BFCA03CE8A0E9FC2C0314C4B096EA30717C03C28CA29E678E63D78ACA1E9A63BDB1261EE7A0B041AB53746D68B57B68BEF37B71382838C95DA8557841A3CA58109F0B4F77A5E929B1A25DC2D6814C55DC0F81CD2F4E5DB95EE70C706FC02C4FCA358EA9A82D8043A47611195580F89458E3DAB5592DEFE06CDE1E516A6C61ED78C13977AE9660A9192CA75CD72967FD3AFAFA1F1A2FF6325A5064D847028F1E6B2329E8572F36E708A549DDA355FC74A32FDD8DBA65
   02 03         ;integer (3 bytes long)
      010001
Run Code Online (Sandbox Code Playgroud)

在 ASN.1 DER 中,每个元素都被编码为类型-长度-值三元组。

类型由标签标识,通常用一个字节进行编码

例如,30表示一个SEQUENCE,而02对应于一种INTEGER类型。

上述文章提供了 ASN.1 定义的不同类型的列表以及相应的标签。

长度有点棘手:如果关联值的长度达到( ) 个字节,则由单个字节指示。如果字节数大于,则第一个长度字节指定实际定义长度的字节数。请考虑阅读本文的这一部分,我认为这是非常有解释性的。1270x7F127Length

然后,您包括实际

在提供的示例中,30 82 01 0A意味着:

  • 30: A SEQUENCE
  • 82:长度字节。0x82 & 0x7F -> 0x02实际值长度以2个字节表示。
  • 01 0A:实际值长度,266字节长。

现在,值02 82 01 01 00 EB506399F5C612F5A67A09C119...

  • 02: 一个INTEGER
  • 82:长度字节。再次,0x82 & 0x7F -> 0x02。实际值长度以2个字节表示。
  • 01 01:实际值长度,257字节长,以第一个字节0x00填充的2048位模(参见前述文章Integer padding中的部分)。
  • 00 EB506399F5C612F5A67A09C119:填充模值。

最后,02 03 010001

  • 02: 一个INTEGER
  • 03:指数值的长度为 3 个字节。
  • 010001:指数值。

然后应将此结果编码为 base64 并用特殊文本分隔符(例如-----BEGIN RSA PUBLIC KEY-----和 )括起来。-----END RSA PUBLIC KEY-----

请注意,您有多种选择来以 PEM 格式表示密钥,即上述一种PKCS#1和更通用的X.509,适用于以不同格式表示密钥(形式 之一-----BEGIN PUBLIC KEY-----) 。考虑阅读这个精彩的SO 问题和相关答案,它不仅详细解释了 RSA 公钥基础知识,还解释了这些不同表示形式的注意事项。

考虑阅读这个,尤其其他问题,我认为它们也有帮助。


Mic*_*rks 5

我创建了一个网站来实现将 PEM 资产转换为 JWK 并返回的特定目的: https: //jwkset.com/generate

您可以在下面看到相关页面的屏幕截图。如果您使用私钥,它是开源的并且易于自行托管。

docker run --rm -p 8080:8080 micahparks/jwksetcom
Run Code Online (Sandbox Code Playgroud)

该网站是https://github.com/MicahParks/jwkset开源项目的一部分,该项目是一个 Golang 包。它还发布了一个 CLI 工具来完成此任务。更多信息请参见网站屏幕截图。

jwkset.com 截图

CLI 工具jwksetinfer可用于将一个或多个 PEM 编码资产转换为单个 JWK 集。

通过 Golang 工具链安装:

go install github.com/MicahParks/jwkset/cmd/jwksetinfer@latest
Run Code Online (Sandbox Code Playgroud)

用法:

jwksetinfer mykey.pem mycert.crt
Run Code Online (Sandbox Code Playgroud)

如果您正在寻找 Golang 代码示例,您可以观察此测试,其中涵盖以下 PEM 编码的 X.509 证书、SEC 1、PKCS #1、PKCS #8 和 PKIX。