如何将 JWK 中的公钥转换为 OpenSSL 的 PEM?
Posted
技术标签:
【中文标题】如何将 JWK 中的公钥转换为 OpenSSL 的 PEM?【英文标题】:How to convert a public key from a JWK into PEM for OpenSSL? 【发布时间】:2017-05-06 23:00:09 【问题描述】:有来自 RFC 的 RSA 密钥:
https://www.rfc-editor.org/rfc/rfc7516#appendix-A.1
"kty":"RSA",
"n":"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW
cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S
psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a
sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS
tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj
YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw",
"e":"AQAB",
"d":"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N
WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9
3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk
qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl
t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd
VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ",
"p":"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-
SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lf
fNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0",
"q":"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBm
UDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aX
IWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc",
"dp":"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KL
hMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827
rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE",
"dq":"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCj
ywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDB
UfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis",
"qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7
AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3
eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"
我试过jwk-to-pem
:
https://***.com/a/35995690/4742108
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAoahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E+BVvxkeDNjbC4
he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S
psk/ZkoFnilakGygTwpZ3uesH+PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenS
ZeyaxziK72UwxrrKoExv6kc5twXTq4h+QChLOln0/mtUZwfsRaMStPs6mS6Xrgxn
xbWhojf663tuEQueGC+FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR/MB/4NUJW/T
qOQtwHYbxevoJArm+L5StowjzGy+/bq6GwIDAQAB
-----END RSA PUBLIC KEY-----
还必须将“RSA PUBLIC KEY”替换为“PUBLIC KEY”。
openssl rsa -inform PEM -pubin
命令给出:
unable to load Public Key
139911798556312:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1197:
139911798556312:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:374:Type=X509_ALGOR
139911798556312:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=algor, Type=X509_PUBKEY
139911798556312:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:pem_oth.c:83:
如何获得可用的密钥?
【问题讨论】:
为了澄清一件事,JWK 包含私钥。任何时候看到“p”、“q”或“d”,则 RSA 密钥包含私有参数。 在线转换工具:keytool.online openssl 在错误跟踪器中存在添加此类功能的问题github.com/openssl/openssl/issues/8240 所以请投票或贡献 github.com/latchset/jose 一个使用 JWT 的工具,基于 OpenSSL 【参考方案1】:我开发了一个 php 类,它能够将公钥/私钥从 JWK 转换为 PEM(反之亦然)。
You will find that class here.
基本上,您必须将 Base64UrlSafe 中的每个组件解码为二进制字符串,并根据RFC3447 中描述的 ASN.1 结构组装所有组件。
不过,我建议您使用专用的库/工具来简化您的工作。 使用我的 PHP 库,您的代码将如下所示:
use Jose\KeyConverter\RSAKey;
$key = new RSAKey([
"kty" => "RSA",
"n" => "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw",
"e" => "AQAB",
"d" => "kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ",
"p" => "1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0",
"q" => "wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc",
"dp" => "ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE",
"dq" => "Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis",
"qi" => "VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY",
]);
$pem = $key->toPEM();
【讨论】:
【参考方案2】:因此,您发布的密钥是公钥和公共指数的简单 asn 序列。它看起来像这样:
SEQUENCE ::=
n Integer,
e Integer
OpenSSL 不喜欢这样,因为它缺少其他一些东西,比如 ObjectIdenifier,以便 openssl 知道密钥用于什么算法。
解决此问题的快速方法是同时输入-RSAPublicKey_in
选项,因此完整的命令将如下所示:
openssl rsa -inform pem -in FILEPATH.pem -pubin -pubout -RSAPublicKey_in
并将文件头改回包含“RSA”:
-----BEGIN RSA PUBLIC KEY-----
还有页脚:
-----END RSA PUBLIC KEY-----
这也会将其输出为“普通”公钥格式,其中包括缺少的 ObjectIdentifier。
注意:我不确定-RSAPublicKey_in
的版本要求是什么,但我使用的是 OpenSSL 1.1.0。
【讨论】:
【参考方案3】:我编写了一个名为lokey 的命令行工具来帮助进行像这样的关键转换。
使用curl
、grep
和tr
从问题中获取密钥,我们可以使用以下命令将JWK格式的私钥转换为PEM格式的私钥:
$ curl -s https://tools.ietf.org/rfc/rfc7516.txt | grep '"n":"oahUI' -B1 -A28 | tr -d '[:space:]' | lokey to pem
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAoahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E+BVvxkeDN
jbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2K
rf3Spsk/ZkoFnilakGygTwpZ3uesH+PFABNIUYpOiN15dsQRkgr0vEhxN92i2asb
OenSZeyaxziK72UwxrrKoExv6kc5twXTq4h+QChLOln0/mtUZwfsRaMStPs6mS6X
rgxnxbWhojf663tuEQueGC+FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR/MB/4NU
JW/TqOQtwHYbxevoJArm+L5StowjzGy+/bq6GwIDAQABAoIBAQCQt20iPoZsOSz8
CkJJNhC16Vw222UqI7I/Mytcd4j7KTUXv6SkPFjj5Zjk1ZXkqe1oR5dLWPzYTfvn
HGFYwdfK+Nh5w9P1+nBH8z2BXyf0euHZqdOlMP3cO3rbKlbfIOwnMGdOeti7WLBZ
GAEqsRhjjqoBkisDbEDCebZfu4ZHWGCGSoOnRWqPeRtILPVfJ8Kzr8t6EHC3EcjK
HxGKnLjSnbiag4BuDFXDevFP++W3dRV7hY7cmQk7OWlR/4pNUjY+2Cb50BHFMS0T
6J37g4mvSH4r5UWzdVKd1VMjOdLF/KuPwgsvowb9S/xgC7tUgtIHeU5bTzn7ioTc
POCtOODJAoGBANa+dl5OOnPi7HweT+ONqe2rXT1K8UEsuJrYv7WSNzIH9hLhVW1K
Awb2kUPklsZG5JqBMy+yV66EgY0qyt2CEIGHTIMgBD0z417/4SNv9Xk0j1WX8y9J
X3zZ9NyF6lheTAxbqFGBNIc29r4a5Tf6yq23wyMFG06444InkW8AugpdAoGBAMC2
9+ce4ZkFmbFiZgf75XvO4oYGdfUvJQRETiyLGRuGsmVXvR4vYA1QjgZlA3Wg2FU0
jcf/BcsNWGGvVxqxpw3sNNCdvHEVKHgQf4yiNaJmtDq79U/WlyFsSsHeIL8RSJjl
q90ES/ShAUc5NkWrVAZ5g2SUyAH8E7NIOvFGR77XAoGAZK+YwE7diUh0qR1tR7w8
WHtolDx3MZ/OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo+uz+KUJWDxS5pFQ/M0evdo
1dKiRTjVw/x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznj
nd/zVpAmZZq60WPMBMfKcuECgYAOrSB+AnUN0UZeIu9ARme4oQ3RRSawkHGSPJ1z
ePhlh0GIxEDLzrHS8cKPLBwyVhiDjKgLhhlr2E1VyyOoDcV6IvX2uTyDpCfb1O3R
yPyqrIWnP280MFR8ICIlV4RI6MkNk9gd0djy/Vv6j5nZfm28vH5MJ6R/ujHJ4oNS
opaOKwKBgFSDKTGGz3+O3U9cP8w1F3z4prEnoJzDgNc02l+2hpP/Lq2OkfPmVtRc
VOwJVgMi1sm6qurdelRXe4UHdMun5VCuEpTsnQetYMGpzrFxjgDr/NnB6tBN3i+B
67WZFLir5z6zJ7FFBZX1Hcm1c8co8/SRf18mmNeGq3S0o3+C/xTW
-----END RSA PRIVATE KEY-----
lokey 还有一个fetch
命令,可用于从 OpenID 端点获取 JWK 密钥:
$ lokey fetch jwk example.okta.com
$ lokey fetch jwk login.salesforce.com
$ lokey fetch jwk accounts.google.com
然后您可以再次将此输出通过管道传输到 lokey
以获取 PEM:
$ lokey fetch jwk example.okta.com | lokey to pem
【讨论】:
我得到了这个错误 Traceback (最近一次调用最后): File "/usr/local/bin/lokey", line 7, in一些将 JWK 转换为 PEM 的 python 代码
import jwt
from cryptography.hazmat.primitives import serialization
def GetClaim(webtoken):
webkey = 'insert jwk here'
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(webkey)
pubk_bytes = public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo)
claim = jwt.decode(webtoken, pubk_bytes, algorithms=['RS256'])
return claim
【讨论】:
对某个答案的一些评论,这意味着某个问题,这个问题,被某人回答了。解释你的一些答案。【参考方案5】:我写了一个Swift library,它能够将公钥/私钥从 JWK 转换为 PEM PKCS#8 编码。
您可以通过以下方式使用它:
import JWKTransform
let key = try RSAKey(jwk: token)
let publicPem = try key.getPublicKey()
let privatePem = try key.getPrivateKey()
关于实际的 JWK,您包含的 RSA 字段的含义如下:
参数n
:Base64 URL 编码字符串,表示 RSA 密钥的modulus
。
参数e
:Base64 URL 编码字符串,表示 RSA 密钥的public exponent
。
参数d
:Base64 URL 编码字符串,表示 RSA 密钥的private exponent
。
参数p
:Base64 URL 编码字符串,表示 RSA 密钥的secret prime factor
。
参数q
:Base64 URL 编码字符串,表示 RSA 密钥的secret prime factor
。
参数dp
:Base64 URL 编码字符串,表示 RSA 密钥的first factor CRT exponent
。 d mod (p-1)
参数dq
:Base64 URL 编码字符串,表示 RSA 密钥的second factor CRT exponent
。 d mod (q-1)
参数qi
:Base64 URL 编码字符串,表示RSA 密钥的first CRT coefficient
。 q^-1 mod p
我在每个参数旁边都包含了,它是 OpenSSL 的 RSA 结构中的对应字段。这只是在您想直接处理 OpenSSL 的情况下:-)
另请注意,如果您将使用引用库生成的密钥与 OpenSSL 生成的 RSA 密钥进行比较:
public key:
这个库应该生成 OpenSSL 生成的公钥。
private key:
RSA 私钥只需要q
,但在提供上述其余值时,RSA 操作通常要快得多。 OpenSSL 生成的 RSA 私钥文件包括这些值。因此,如果未提供所有私有参数,则生成的私有密钥可能与生成的原始 OpenSSL 不完全匹配。
【讨论】:
想添加椭圆曲线 JWK @channel? 我会调查的。以上是关于如何将 JWK 中的公钥转换为 OpenSSL 的 PEM?的主要内容,如果未能解决你的问题,请参考以下文章
openssl_verify():提供的密钥参数不能被强制转换为 .pem 文件的公钥
如何从 .cer 中提取 RSA 公钥并使用 OpenSSL 将其存储在 .pem 中?
sh 使用openssl为JWT JWK(JSON Web Token JSON Web Key)生成ES512和RS256椭圆曲线密钥对