JWT RSA signing with Python JOSE

By John Keyes

August 2, 2024 at 07:32

jwt jwt rsa python

Background

If you are unfamiliar with JWTs, please check out A Primer on JSON Web Tokens.

Generating RSA keys using openssh

See RSA key formatting for details on how to create a key pair, and how to transform them into the correct format for python-jose.

Signing with the private key

The private key can be used by python-jose to sign a token:

from jose import jwt, jws

# to encode we use the private key
with open("request_key.pkcs8", "r") as f:
    key = f.read()
    token = jwt.encode({"a": "b"}, key, algorithm="RS256")
    # see the token
    print(token)

When this code is run a JWT string is output:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.mAd3jmz3x_HKZyf5TyNSjg9ftJ4fBxM9_k4ZwA-f3TsAOzd9JLmJMfrye_MGVYheR-MORnsGEK0QTDgqGXAHOvD6GlWi5N8TePioodPIbNRl3IiXuGCdib-IeVwin30Y7JQqvKo80TfZPWaIGspMuaRVmyPkFX1510_LSpYy5IEz48PaeJK-ZkKS6O3prfLxWN6M8Y4sxLcnyPBMM4wBk-9gOk3LDv3TDAl9hRc3L7om8IMpFWqha5vOGUDsS_WvaK-av94VIN0YaXLvbe-VZAM7FVKFrsHlOPWlJBb5X1b1AiwDMkjiv3ApNzQzA5XC9ZdrkARPeK0IyTbwI-GETqyjdxeIxot_1PZma_45mPM82vrOg4EepNiAKfpwbOOWKIIB0tcK5yVZDP4Yrgqod6pvRT2Snf3OAxkjNcjtIWhosBjQ-Sqy3w0ESoLXq1HG_jdjS5oSRV0PJE6wwNbSILxmmAJErtslsCYZrbUVeclRWBE_bayYLqVK3bU4KFux

Verifying with the public key

The public key can be used to verify this token was signed by the private key:

from jose import jwt, jws

token = "<token from signing step>"

# to verify/decode we use the public key
with open("request_key.pub", "r") as f:
    pub_key = f.read()
    print(jws.verify(token, pub_key, algorithms=["RS256"]))
    print(jwt.decode(token, pub_key, algorithms=["RS256"]))

The following is the output when we run this:

b'{"a":"b"}'
{'a': 'b'}

If a different key is used for encoding we can see the verify call raises an error:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/jose/jws.py", line 262, in _verify_signature
    raise JWSSignatureError()
jose.exceptions.JWSSignatureError


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/jose/jws.py", line 73, in verify
    _verify_signature(signing_input, header, signature, key, algorithms)
...

It’s also worth noting that the python-jose decode function verifies the signature by default. With the verfiy op removed from the code this is the output when the encoding key has been changed:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/jose/jws.py", line 262, in _verify_signature
    raise JWSSignatureError()
jose.exceptions.JWSSignatureError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/jose/jwt.py", line 142, in decode
    payload = jws.verify(token, key, algorithms, verify=verify_signature)
...

Summary

This is a brief intro to RSA signing JWTs in python-jose, which will hopefully shortcut any future work where we are dealing with JWTs in Python.

Last updated: August 2, 2024 at 07:32