How to convert a public key from a JWK into PEM for OpenSSL?

50.6k views Asked by At

There is an RSA key from an RFC:

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"
 }

I've tried jwk-to-pem:

https://stackoverflow.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-----

Also had to replace "RSA PUBLIC KEY" with "PUBLIC KEY".

The command openssl rsa -inform PEM -pubin gives:

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:

How to get a usable key?

7

There are 7 answers

0
vcsjones On BEST ANSWER

So the key that you posted is a simple asn sequence of a a public key and the public exponent. It looks something like this:

SEQUENCE ::= {
    n Integer,
    e Integer
}

OpenSSL doesn't like that as-is because it's missing a few other things, like an ObjectIdenifier so that openssl knows what algorithm the key is for.

The quick way to fix this is to also put in the -RSAPublicKey_in option, so the full command will look something like this:

openssl rsa -inform pem -in FILEPATH.pem -pubin -pubout -RSAPublicKey_in

and change the header of the file back to include "RSA":

-----BEGIN RSA PUBLIC KEY-----

as well as the footer:

-----END RSA PUBLIC KEY-----

This will also output it in to a "normal" public key format that includes the missing ObjectIdentifier.

Note: I'm not sure what the version requirement for -RSAPublicKey_in are, but I was using OpenSSL 1.1.0.

0
Spomky-Labs On

I developed a a PHP class that is able to convert public/private keys from JWK to PEM (and vice versa).

You will find that class here.

Basically, you have to decode each component from Base64UrlSafe to a binary string and assemble all of them according to the ASN.1 Structure described in the RFC3447.

Nevertheless, I recommend you to use a dedicated library/tool for that to ease your work. With my PHP library, your code will looks like:

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();
1
Jeffrey DeMuth On

Some python code to convert a JWK to PEM


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
0
Arnaud Grandville On

Translate a jwk to RSA key with openssl

  1. create a private key asn1 definition file privkey.asn1
asn1=SEQUENCE:privkeyinfo

[privkeyinfo]
version=INTEGER:0
algorithm=SEQUENCE:rsa_alg
key=OCTWRAP,SEQUENCE:rsaprivkey

[rsa_alg]
algorithm=OID:rsaEncryption
parameter=NULL

[rsaprivkey]
version=INTEGER:0

# modulus
n=INTEGER:0xa1a8542285b0d0ad2eb0a36e391e87e3091fe280545c74f146f81be3c13e055bf191e0cd8db0b885ef2b516709a1999db3687b33bd22984561454e5d8c8357b6a9655c8e0316a708d438234fd2dec0d0fc316d8aadfdd2a6c93f664a059e295a906ca04f0a59dee7ac1fe3c5001348518a4e88dd7976c411920af4bc487137dda2d9ab1b39e9d265ec9ac7388aef6530c6bacaa04c6fea4739b705d3ab887e40284b3a59f4fe6b546707ec45a312b4fb3a992e97ae0c67c5b5a1a237faeb7b6e110b9e182f8508c7eb6b7e82f649c3146ccab0d6bb2d92b6763620c83dc947f301ff8354256fd3a8e42dc0761bc5ebe8240ae6f8be52b68c23cc6cbefdbaba1b

# publicExponent
e=INTEGER:0x010001

# privateExponent
d=INTEGER:0xa1a8542285b0d0ad2eb0a36e391e87e3091fe280545c74f146f81be3c13e055bf191e0cd8db0b885ef2b516709a1999db3687b33bd22984561454e5d8c8357b6a9655c8e0316a708d438234fd2dec0d0fc316d8aadfdd2a6c93f664a059e295a906ca04f0a59dee7ac1fe3c5001348518a4e88dd7976c411920af4bc487137dda2d9ab1b39e9d265ec9ac7388aef6530c6bacaa04c6fea4739b705d3ab887e40284b3a59f4fe6b546707ec45a312b4fb3a992e97ae0c67c5b5a1a237faeb7b6e110b9e182f8508c7eb6b7e82f649c3146ccab0d6bb2d92b6763620c83dc947f301ff8354256fd3a8e42dc0761bc5ebe8240ae6f8be52b68c23cc6cbefdbaba1b

# prime1
p=INTEGER:0xd6be765e4e3a73e2ec7c1e4fe38da9edab5d3d4af1412cb89ad8bfb592373207f612e1556d4a0306f69143e496c646e49a81332fb257ae84818d2acadd821081874c8320043d33e35effe1236ff579348f5597f32f495f7cd9f4dc85ea585e4c0c5ba85181348736f6be1ae537facaadb7c323051b4eb8e38227916f00ba0a5d

# prime2
q=INTEGER:0xc0b6f7e71ee1990599b1626607fbe57bcee2860675f52f2504444e2c8b191b86b26557bd1e2f600d508e06650375a0d855348dc7ff05cb0d5861af571ab1a70dec34d09dbc71152878107f8ca235a266b43abbf54fd697216c4ac1de20bf114898e5abdd044bf4a10147393645ab540679836494c801fc13b3483af14647bed7

# exponent1 
dp=INTEGER:0x64af98c04edd894874a91d6d47bc3c587b68943c77319fce4e8c2216f81f790dd28ab7ac5e39bd819e4a2e1317be8faecfe2942560f14b9a4543f3347af768d5d2a24538d5c3fc78372ab25cf3399d42cf91ca54f36eeb9e96730092a976158082aad718a0040901f45ede2d6b39e39ddff3569026659abad163cc04c7ca72e1

# exponent2
dq=INTEGER:0x0ead207e02750dd1465e22ef404667b8a10dd14526b09071923c9d7378f865874188c440cbceb1d2f1c28f2c1c325618838ca80b86196bd84d55cb23a80dc57a22f5f6b93c83a427dbd4edd1c8fcaaac85a73f6f3430547c202225578448e8c90d93d81dd1d8f2fd5bfa8f99d97e6dbcbc7e4c27a47fba31c9e28352a2968e2b

# coefficient
qi=INTEGER:0x5483293186cf7f8edd4f5c3fcc35177cf8a6b127a09cc380d734da5fb68693ff2ead8e91f3e656d45c54ec09560322d6c9baaaeadd7a54577b850774cba7e550ae1294ec9d07ad60c1a9ceb1718e00ebfcd9c1ead04dde2f81ebb59914b8abe73eb327b1450595f51dc9b573c728f3f4917f5f2698d786ab74b4a37f82ff14d6
  1. create the private key
openssl asn1parse -genconf privkey.asn1 -out privkey.der -noout -inform der
  1. The private key privkey.der contents
openssl rsa -in privkey.der -inform der -text -noout
RSA Private-Key: (2048 bit, 2 primes)
modulus:
    00:a1:a8:54:22:85:b0:d0:ad:2e:b0:a3:6e:39:1e:
    87:e3:09:1f:e2:80:54:5c:74:f1:46:f8:1b:e3:c1:
    3e:05:5b:f1:91:e0:cd:8d:b0:b8:85:ef:2b:51:67:
    09:a1:99:9d:b3:68:7b:33:bd:22:98:45:61:45:4e:
    5d:8c:83:57:b6:a9:65:5c:8e:03:16:a7:08:d4:38:
    23:4f:d2:de:c0:d0:fc:31:6d:8a:ad:fd:d2:a6:c9:
    3f:66:4a:05:9e:29:5a:90:6c:a0:4f:0a:59:de:e7:
    ac:1f:e3:c5:00:13:48:51:8a:4e:88:dd:79:76:c4:
    11:92:0a:f4:bc:48:71:37:dd:a2:d9:ab:1b:39:e9:
    d2:65:ec:9a:c7:38:8a:ef:65:30:c6:ba:ca:a0:4c:
    6f:ea:47:39:b7:05:d3:ab:88:7e:40:28:4b:3a:59:
    f4:fe:6b:54:67:07:ec:45:a3:12:b4:fb:3a:99:2e:
    97:ae:0c:67:c5:b5:a1:a2:37:fa:eb:7b:6e:11:0b:
    9e:18:2f:85:08:c7:eb:6b:7e:82:f6:49:c3:14:6c:
    ca:b0:d6:bb:2d:92:b6:76:36:20:c8:3d:c9:47:f3:
    01:ff:83:54:25:6f:d3:a8:e4:2d:c0:76:1b:c5:eb:
    e8:24:0a:e6:f8:be:52:b6:8c:23:cc:6c:be:fd:ba:
    ba:1b
publicExponent: 65537 (0x10001)
privateExponent:
    00:a1:a8:54:22:85:b0:d0:ad:2e:b0:a3:6e:39:1e:
    87:e3:09:1f:e2:80:54:5c:74:f1:46:f8:1b:e3:c1:
    3e:05:5b:f1:91:e0:cd:8d:b0:b8:85:ef:2b:51:67:
    09:a1:99:9d:b3:68:7b:33:bd:22:98:45:61:45:4e:
    5d:8c:83:57:b6:a9:65:5c:8e:03:16:a7:08:d4:38:
    23:4f:d2:de:c0:d0:fc:31:6d:8a:ad:fd:d2:a6:c9:
    3f:66:4a:05:9e:29:5a:90:6c:a0:4f:0a:59:de:e7:
    ac:1f:e3:c5:00:13:48:51:8a:4e:88:dd:79:76:c4:
    11:92:0a:f4:bc:48:71:37:dd:a2:d9:ab:1b:39:e9:
    d2:65:ec:9a:c7:38:8a:ef:65:30:c6:ba:ca:a0:4c:
    6f:ea:47:39:b7:05:d3:ab:88:7e:40:28:4b:3a:59:
    f4:fe:6b:54:67:07:ec:45:a3:12:b4:fb:3a:99:2e:
    97:ae:0c:67:c5:b5:a1:a2:37:fa:eb:7b:6e:11:0b:
    9e:18:2f:85:08:c7:eb:6b:7e:82:f6:49:c3:14:6c:
    ca:b0:d6:bb:2d:92:b6:76:36:20:c8:3d:c9:47:f3:
    01:ff:83:54:25:6f:d3:a8:e4:2d:c0:76:1b:c5:eb:
    e8:24:0a:e6:f8:be:52:b6:8c:23:cc:6c:be:fd:ba:
    ba:1b
prime1:
    00:d6:be:76:5e:4e:3a:73:e2:ec:7c:1e:4f:e3:8d:
    a9:ed:ab:5d:3d:4a:f1:41:2c:b8:9a:d8:bf:b5:92:
    37:32:07:f6:12:e1:55:6d:4a:03:06:f6:91:43:e4:
    96:c6:46:e4:9a:81:33:2f:b2:57:ae:84:81:8d:2a:
    ca:dd:82:10:81:87:4c:83:20:04:3d:33:e3:5e:ff:
    e1:23:6f:f5:79:34:8f:55:97:f3:2f:49:5f:7c:d9:
    f4:dc:85:ea:58:5e:4c:0c:5b:a8:51:81:34:87:36:
    f6:be:1a:e5:37:fa:ca:ad:b7:c3:23:05:1b:4e:b8:
    e3:82:27:91:6f:00:ba:0a:5d
prime2:
    00:c0:b6:f7:e7:1e:e1:99:05:99:b1:62:66:07:fb:
    e5:7b:ce:e2:86:06:75:f5:2f:25:04:44:4e:2c:8b:
    19:1b:86:b2:65:57:bd:1e:2f:60:0d:50:8e:06:65:
    03:75:a0:d8:55:34:8d:c7:ff:05:cb:0d:58:61:af:
    57:1a:b1:a7:0d:ec:34:d0:9d:bc:71:15:28:78:10:
    7f:8c:a2:35:a2:66:b4:3a:bb:f5:4f:d6:97:21:6c:
    4a:c1:de:20:bf:11:48:98:e5:ab:dd:04:4b:f4:a1:
    01:47:39:36:45:ab:54:06:79:83:64:94:c8:01:fc:
    13:b3:48:3a:f1:46:47:be:d7
exponent1:
    64:af:98:c0:4e:dd:89:48:74:a9:1d:6d:47:bc:3c:
    58:7b:68:94:3c:77:31:9f:ce:4e:8c:22:16:f8:1f:
    79:0d:d2:8a:b7:ac:5e:39:bd:81:9e:4a:2e:13:17:
    be:8f:ae:cf:e2:94:25:60:f1:4b:9a:45:43:f3:34:
    7a:f7:68:d5:d2:a2:45:38:d5:c3:fc:78:37:2a:b2:
    5c:f3:39:9d:42:cf:91:ca:54:f3:6e:eb:9e:96:73:
    00:92:a9:76:15:80:82:aa:d7:18:a0:04:09:01:f4:
    5e:de:2d:6b:39:e3:9d:df:f3:56:90:26:65:9a:ba:
    d1:63:cc:04:c7:ca:72:e1
exponent2:
    0e:ad:20:7e:02:75:0d:d1:46:5e:22:ef:40:46:67:
    b8:a1:0d:d1:45:26:b0:90:71:92:3c:9d:73:78:f8:
    65:87:41:88:c4:40:cb:ce:b1:d2:f1:c2:8f:2c:1c:
    32:56:18:83:8c:a8:0b:86:19:6b:d8:4d:55:cb:23:
    a8:0d:c5:7a:22:f5:f6:b9:3c:83:a4:27:db:d4:ed:
    d1:c8:fc:aa:ac:85:a7:3f:6f:34:30:54:7c:20:22:
    25:57:84:48:e8:c9:0d:93:d8:1d:d1:d8:f2:fd:5b:
    fa:8f:99:d9:7e:6d:bc:bc:7e:4c:27:a4:7f:ba:31:
    c9:e2:83:52:a2:96:8e:2b
coefficient:
    54:83:29:31:86:cf:7f:8e:dd:4f:5c:3f:cc:35:17:
    7c:f8:a6:b1:27:a0:9c:c3:80:d7:34:da:5f:b6:86:
    93:ff:2e:ad:8e:91:f3:e6:56:d4:5c:54:ec:09:56:
    03:22:d6:c9:ba:aa:ea:dd:7a:54:57:7b:85:07:74:
    cb:a7:e5:50:ae:12:94:ec:9d:07:ad:60:c1:a9:ce:
    b1:71:8e:00:eb:fc:d9:c1:ea:d0:4d:de:2f:81:eb:
    b5:99:14:b8:ab:e7:3e:b3:27:b1:45:05:95:f5:1d:
    c9:b5:73:c7:28:f3:f4:91:7f:5f:26:98:d7:86:ab:
    74:b4:a3:7f:82:ff:14:d6
  1. Your private key pem encoded
openssl rsa -in privkey.der -inform der
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAoahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E+BVvxkeDN
jbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2K
rf3Spsk/ZkoFnilakGygTwpZ3uesH+PFABNIUYpOiN15dsQRkgr0vEhxN92i2asb
OenSZeyaxziK72UwxrrKoExv6kc5twXTq4h+QChLOln0/mtUZwfsRaMStPs6mS6X
rgxnxbWhojf663tuEQueGC+FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR/MB/4NU
JW/TqOQtwHYbxevoJArm+L5StowjzGy+/bq6GwIDAQABAoIBAQChqFQihbDQrS6w
o245HofjCR/igFRcdPFG+BvjwT4FW/GR4M2NsLiF7ytRZwmhmZ2zaHszvSKYRWFF
Tl2Mg1e2qWVcjgMWpwjUOCNP0t7A0PwxbYqt/dKmyT9mSgWeKVqQbKBPClne56wf
48UAE0hRik6I3Xl2xBGSCvS8SHE33aLZqxs56dJl7JrHOIrvZTDGusqgTG/qRzm3
BdOriH5AKEs6WfT+a1RnB+xFoxK0+zqZLpeuDGfFtaGiN/rre24RC54YL4UIx+tr
foL2ScMUbMqw1rstkrZ2NiDIPclH8wH/g1Qlb9Oo5C3AdhvF6+gkCub4vlK2jCPM
bL79urobAoGBANa+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-----
2
channel On

I wrote a Swift library that is able to convert public/private keys from JWK to PEM PKCS#8 encoding.

You can use it by:

import JWKTransform

let key = try RSAKey(jwk: token)
let publicPem = try key.getPublicKey()
let privatePem = try key.getPrivateKey()

Regarding the actual JWK, the RSA fields you included mean the following:

  • parameter n: Base64 URL encoded string representing the modulus of the RSA Key.
  • parameter e: Base64 URL encoded string representing the public exponent of the RSA Key.
  • parameter d: Base64 URL encoded string representing the private exponent of the RSA Key.
  • parameter p: Base64 URL encoded string representing the secret prime factor of the RSA Key.
  • parameter q: Base64 URL encoded string representing the secret prime factor of the RSA Key.
  • parameter dp: Base64 URL encoded string representing the first factor CRT exponent of the RSA Key. d mod (p-1)
  • parameter dq: Base64 URL encoded string representing the second factor CRT exponent of the RSA Key. d mod (q-1)
  • parameter qi: Base64 URL encoded string representing the first CRT coefficient of the RSA Key. q^-1 mod p

I have included next to each parameter, it's corresponding field in OpenSSL's RSA structure. This is only in case you want to deal with OpenSSL directly :-)

Also note that if you compare the keys that are produced using the referenced library with OpenSSL-generated RSA keys:

  • public key: This library should produce the public key that OpenSSL generates.

  • private key: RSA private key only requires q but RSA operations are generally much faster when the rest of the values above are provided. The OpenSSL generated RSA private key files includes these values. Therefore if not all private paramters are provided, then the produced private key might not be an exact match to the original OpenSSL generated.

0
Lorimer On

With that library you need to specify its a private key like this in JS

const private_pem = jwkToPem(jwk, {private: true})
1
Joël Franusic On

I wrote a command line tool called lokey to help with key conversions like this one.

Using curl, grep, and tr to grab the key from question, we can convert the JWK formatted private key into a PEM formatted private key with this command:

$ 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 also has a fetch command that can be used to fetch JWK keys from OpenID endpoints:

$ lokey fetch jwk example.okta.com
$ lokey fetch jwk login.salesforce.com
$ lokey fetch jwk accounts.google.com

You can then pipe this output into lokey again to get a PEM:

$ lokey fetch jwk example.okta.com | lokey to pem