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.