python-jwt
Module for generating and verifying JSON Web Tokens.
- Note: Versions 3.3.4 and later fix a vulnerability (CVE-2022-39227) in JSON Web Token verification which lets an attacker with a valid token re-use its signature with modified claims. CVE to follow. Please upgrade!
- Note: From version 2.0.1 the namespace has changed from jwt to python_jwt , in order to avoid conflict with PyJWT.
- Note: Versions 1.0.0 and later fix a vulnerability in JSON Web Token verification so please upgrade if you’re using this functionality. The API has changed so you will need to update your application. verify_jwt now requires you to specify which signature algorithms are allowed.
- Uses jwcrypto to do the heavy lifting.
- Supports RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512, ES256K, EdDSA and none signature algorithms.
- Unit tests, including tests for interoperability with jose.
- Supports Python 3.6+. Note:generate_jwt returns the token as a Unicode string.
Installation
Another Example
You can read and write keys from and to PEM-format strings:
Tests
Code Coverage
Benchmarks
Here are some results on a laptop with an Intel Core i5-4300M 2.6Ghz CPU and 8Gb RAM running Ubuntu 17.04.
Generate Key | user (ns) | sys (ns) | real (ns) |
---|---|---|---|
RSA | 103,100,000 | 200,000 | 103,341,537 |
Generate Token | user (ns) | sys (ns) | real (ns) |
---|---|---|---|
HS256 | 220,000 | 0 | 226,478 |
HS384 | 220,000 | 0 | 218,233 |
HS512 | 230,000 | 0 | 225,823 |
PS256 | 1,530,000 | 10,000 | 1,536,235 |
PS384 | 1,550,000 | 0 | 1,549,844 |
PS512 | 1,520,000 | 10,000 | 1,524,844 |
RS256 | 1,520,000 | 10,000 | 1,524,565 |
RS384 | 1,530,000 | 0 | 1,528,074 |
RS512 | 1,510,000 | 0 | 1,526,089 |
Load Key | user (ns) | sys (ns) | real (ns) |
---|---|---|---|
RSA | 210,000 | 3,000 | 210,791 |
Verify Token | user (ns) | sys (ns) | real (ns) |
---|---|---|---|
HS256 | 100,000 | 0 | 101,478 |
HS384 | 100,000 | 10,000 | 103,014 |
HS512 | 110,000 | 0 | 104,323 |
PS256 | 230,000 | 0 | 231,058 |
PS384 | 240,000 | 0 | 237,551 |
PS512 | 240,000 | 0 | 232,450 |
RS256 | 230,000 | 0 | 227,737 |
RS384 | 230,000 | 0 | 230,698 |
RS512 | 230,000 | 0 | 228,624 |
How to convert a public key from a JWK into PEM for OpenSSL?
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:
To clarify one thing though, that JWK includes the private key. Any time to see a «p», «q», or «d» then the RSA key includes private parameters.
The openssl have an issue in bug tracker to add such functionality github.com/openssl/openssl/issues/8240 so please vote or contribute
6 Answers 6
I developed a a PHP class that is able to convert public/private keys from JWK to PEM (and vice versa).
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();
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:
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»:
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.
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