How to verify a JWT using python PyJWT with public key How to verify a JWT using python PyJWT with public key python python

How to verify a JWT using python PyJWT with public key


I'm putting this here for the next person like me that looks for it.

What I needed was:

  1. A Private key that i can keep place behind a service (think AWS API GATEWAY) and generate JWT tokens securely and pass them down to lower services.
  2. A Public key that i can give to any of my micro services/anything else that can validate that the JWT token is valid WITHOUT knowing my Private key

Setup:

  # lets create a key to sign these tokens with  openssl genpkey -out mykey.pem -algorithm rsa -pkeyopt rsa_keygen_bits:2048   # lets generate a public key for it...  openssl rsa -in mykey.pem -out mykey.pub -pubout   # make another key so we can test that we cannot decode from it  openssl genpkey -out notmykey.pem -algorithm rsa -pkeyopt rsa_keygen_bits:2048   # this is really the key we would be using to try to check the signature  openssl rsa -in notmykey.pem -out notmykey.pub -pubout

Code:

import jwtfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives import serialization# Load the key we createdwith open("mykey.pem", "rb") as key_file:    private_key = serialization.load_pem_private_key(        key_file.read(),        password=None,        backend=default_backend()    )# The data we're trying to pass along from place to placedata = {'user_id': 1}# Lets create the JWT token -- this is a byte array, meant to be sent as an HTTP headerjwt_token = jwt.encode(data, key=private_key, algorithm='RS256')print(f'data {data}')print(f'jwt_token {jwt_token}')# Load the public key to run another test...with open("mykey.pub", "rb") as key_file:    public_key = serialization.load_pem_public_key(        key_file.read(),        backend=default_backend()    )# This will prove that the derived public-from-private key is validprint(f'decoded with public key (internal): {jwt.decode(jwt_token, private_key.public_key())}')# This will prove that an external service consuming this JWT token can trust the token # because this is the only key it will have to validate the token.print(f'decoded with public key (external): {jwt.decode(jwt_token, public_key)}')# Lets load another public key to see if we can load the data successfulywith open("notmykey.pub", "rb") as key_file:    not_my_public_key = serialization.load_pem_public_key(        key_file.read(),        backend=default_backend()    )# THIS WILL FAIL!!!!!!!!!!!!!!!!!!!!!!!# Finally, this will not work and cause an exceptionprint(f'decoded with another public key: {jwt.decode(jwt_token, not_my_public_key)}')

More info here: https://gist.github.com/kingbuzzman/3912cc66896be0a06bf0eb23bb1e1999 -- along with a docker example of how to run this quickly


@javier-buzzi's answer returned this error to me:

TypeError: from_buffer() cannot return the address of a unicode object

Here is how I managed to make it work with python-jose

Create a RSA certificate (auth.pem) and it's public key (auth.pub):

openssl genpkey -out auth.pem -algorithm rsa -pkeyopt rsa_keygen_bits:2048 openssl rsa -in auth.pem -out auth.pub -pubout

(Thanks Javier)

from jose import jwtdata = {    "sample" : "data"}# Encode datawith open("auth.pem") as key_file:    token = jwt.encode(data, key=key_file.read(), algorithm='RS256')print(token)# Decode data with only he public keywith open("auth.pub") as pubkey_file:    decoded_data = jwt.decode(token, key=pubkey_file.read(), algorithms='RS256')print(decoded_data)

output:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzYW1wbGUiOiJkYXRhIn0.GnDlS0FRFqdk1CsqFg2adHwSvrL8_JKtk4IQpuAzbjdDIi1xoymxxMIW4QNhl67QHIQrs0NG6lBi7eNfJ69Kgu6j-bY4NVP5-0D03wDrlBNowBPLMQ7RoCiDvtN1gqaTdf6VyNju6m9FmGImneZ84XMX2d1yWzXMSGtL2_8e99BmK0-h3r_o8IF7eSHN1SVxqrIN7vpcgfKcG0QjLZ-kBFpq4kgj5Fcr5coBIMmK6O0jB_4lBsNGa_0GixCXeWXkv_KqAky2yliEzV68lHOBCsBN_ZAjB3kllaIAOJCsQPLdqgXqgpeMQdzktVCVJKMAEYPdlv8mdadJSvxwxT9HBA{'sample': 'data'}


This other library (python-jose) may help verifying.

Notice that keys must be a JSON dict to be passed to decode.