How to verify a JWT using python PyJWT with public key

pyjwt could not deserialize key data
pyjwt example
python decode jwt without secret
jwt public key
pyjwt github
python jwt github
generate jwt token with private key c#
jwt secret key generator

I've been struggling to get PyJWT 1.1.0 verify a JWT with public key. These keys are the defaults shipped with Keycloak. Most likely the problem is related to the creation of the secret key, but I haven't found any working examples for creating the key without a certificate with both private and public key.

Here's my attempts to get it working. Some of the tests below complain about invalid key and some of them complain that the token is not verified properly against the key.

import jwt

from cryptography.hazmat.backends import default_backend
from itsdangerous import base64_decode
from Crypto.PublicKey import RSA


secret = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIE6a1NyEFe7qCDFrvWFZiAlY1ttE5596w5dLjNSaHlKGv8AXbKg/f8yKY9fKAJ5BKoeWEkPPjpn1t9QQAZYzqH9KNOFigMU8pSaRUxjI2dDvwmu8ZH6EExY+RfrPjQGmeliK18iFzFgBtf0eH3NAW3Pf71OZZz+cuNnVtE9lrYQIDAQAB"
secretDer = base64_decode(secret)
sshrsaSecret = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCIE6a1NyEFe7qCDFrvWFZiAlY1ttE5596w5dLjNSaHlKGv8AXbKg/f8yKY9fKAJ5BKoeWEkPPjpn1t9QQAZYzqH9KNOFigMU8pSaRUxjI2dDvwmu8ZH6EExY+RfrPjQGmeliK18iFzFgBtf0eH3NAW3Pf71OZZz+cuNnVtE9lrYQ=="
secretPEM = "-----BEGIN PUBLIC KEY-----\n" + secret + "\n-----END PUBLIC KEY-----"
access_token = "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIzM2ZhZGYzMS04MzZmLTQzYWUtODM4MS01OGJhM2RhMDMwYTciLCJleHAiOjE0MjkwNzYyNTYsIm5iZiI6MCwiaWF0IjoxNDI5MDc2MTk2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODEvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoic2VjdXJpdHktYWRtaW4tY29uc29sZSIsInN1YiI6ImMzNWJlODAyLTcyOGUtNGMyNC1iMjQ1LTQxMWIwMDRmZTc2NSIsImF6cCI6InNlY3VyaXR5LWFkbWluLWNvbnNvbGUiLCJzZXNzaW9uX3N0YXRlIjoiYmRjOGM0ZDgtYzUwNy00MDQ2LWE4NDctYmRlY2QxNDVmZTNiIiwiY2xpZW50X3Nlc3Npb24iOiI0OTI5YmRjNi0xOWFhLTQ3MDYtYTU1Mi1lOWI0MGFhMDg5ZTYiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYWRtaW4iLCJjcmVhdGUtcmVhbG0iXX0sInJlc291cmNlX2FjY2VzcyI6eyJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1jbGllbnRzIiwidmlldy1yZWFsbSIsInZpZXctZXZlbnRzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS11c2VycyIsIm1hbmFnZS1yZWFsbSJdfX0sIm5hbWUiOiIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.O7e8dkv0k-2HCjMdZFXIxLhypVyRPwIdrQsYTMwC1996wbsjIw1L3OjDSzJKXcx0U9YrVeRM4yMVlFg40uJDC-9IsKZ8nr5dl_da8SzgpAkempxpas3girST2U9uvY56m2Spp6-EFInvMSb6k4t1L49_Q7R2g0DOlKzxgQd87LY"

############### Test using PEM key (with ----- lines)
try:
    access_token_json = jwt.decode(access_token, key=secretPEM)
except Exception as e:
    print "Not working using PEM key with ----: ", e
else:
    print "It worked!"

############### Test using PEM key (without ----- lines)
try:
    access_token_json = jwt.decode(access_token, key=secret)
except Exception as e:
    print "Not working using PEM key without ----: ", e
else:
    print "It worked!"

############### Test using DER key
try:
    access_token_json = jwt.decode(access_token, key=secretDer)
except Exception as e:
    print "Not working using DER key: ", e
else:
    print "It worked!"

############### Test using DER key #2
try:
    public_key = default_backend().load_der_public_key(secretDer)
    access_token_json = jwt.decode(access_token, key=public_key)
except Exception as e:
    print "Not working using DER key #2: ", e
else:
    print "It worked!"

############### Test using SSH style key
try:
    access_token_json = jwt.decode(access_token, key=sshrsaSecret)
except Exception as e:
    print "Not working using SSH style key: ", e
else:
    print "It worked!"

############### Test using RSA numbers
class Numbers:
    pass

numbers = Numbers()
public_key = RSA.importKey(secretDer)
numbers.e = public_key.key.e
numbers.n = public_key.key.n
# yet another way to generated valid key object
public_key = default_backend().load_rsa_public_numbers(numbers)
print public_key
try:
    access_token_json = jwt.decode(access_token, key=public_key)
except Exception as e:
    print "Not working using RSA numbers: ", e
else:
    print "It worked!"
###############

I have checked that the token and key are working with Java implementation, see below.

import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;

public class JWTTest {
    public static final void main(String[] argv) {
        String token = "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIzM2ZhZGYzMS04MzZmLTQzYWUtODM4MS01OGJhM2RhMDMwYTciLCJleHAiOjE0MjkwNzYyNTYsIm5iZiI6MCwiaWF0IjoxNDI5MDc2MTk2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODEvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoic2VjdXJpdHktYWRtaW4tY29uc29sZSIsInN1YiI6ImMzNWJlODAyLTcyOGUtNGMyNC1iMjQ1LTQxMWIwMDRmZTc2NSIsImF6cCI6InNlY3VyaXR5LWFkbWluLWNvbnNvbGUiLCJzZXNzaW9uX3N0YXRlIjoiYmRjOGM0ZDgtYzUwNy00MDQ2LWE4NDctYmRlY2QxNDVmZTNiIiwiY2xpZW50X3Nlc3Npb24iOiI0OTI5YmRjNi0xOWFhLTQ3MDYtYTU1Mi1lOWI0MGFhMDg5ZTYiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYWRtaW4iLCJjcmVhdGUtcmVhbG0iXX0sInJlc291cmNlX2FjY2VzcyI6eyJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1jbGllbnRzIiwidmlldy1yZWFsbSIsInZpZXctZXZlbnRzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS11c2VycyIsIm1hbmFnZS1yZWFsbSJdfX0sIm5hbWUiOiIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.O7e8dkv0k-2HCjMdZFXIxLhypVyRPwIdrQsYTMwC1996wbsjIw1L3OjDSzJKXcx0U9YrVeRM4yMVlFg40uJDC-9IsKZ8nr5dl_da8SzgpAkempxpas3girST2U9uvY56m2Spp6-EFInvMSb6k4t1L49_Q7R2g0DOlKzxgQd87LY";
        String key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHJUdDw1bPg/tZBY+kDDZZQnAp1mVr0CMyE+VzvJ+n2v6SHBdjjuWEw+LfLd69evg8ndr1RRPWZ1ryKgWS/NKTNqH+UhHkK9NToDucJI9Bi/scCpBps+/X/S7gZtcBMdfd4IB+LPCsP8v2RT/H9VjeCP4sWuqNwAMtCMyGr1Vw9wIDAQAB";
        String verifierKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----";
        SignatureVerifier verifier = new RsaVerifier(verifierKey);
        System.out.println(JwtHelper.decodeAndVerify(token, verifier));
    }
}

Update: I'm able to sign a token properly with HS256 (verified with http://jwt.io/) using the following code. However, I'm unable to decode the PyJWT signed token using PyJWT. The interface is really weird. Here example (secret is the same as in above examples):

some_token = jwt.encode(access_token_json, secret)
# verified some_token to be valid with jwt.io
# the code below does not validate the token correctly
jwt.decode(some_token, key=secret)

Update 2: This works

from jwt.algorithms import HMACAlgorithm, RSAAlgorithm
access_token_json = jwt.decode(access_token, verify=False)
algo = HMACAlgorithm(HMACAlgorithm.SHA256)
shakey = algo.prepare_key(secret)
testtoken = jwt.encode(access_token_json, key=shakey, algorithm='HS256')
options={'verify_exp': False,  # Skipping expiration date check
         'verify_aud': False } # Skipping audience check
print jwt.decode(testtoken, key=shakey, options=options)

However, this does not

from jwt.algorithms import HMACAlgorithm, RSAAlgorithm
algo = RSAAlgorithm(RSAAlgorithm.SHA256)
shakey = algo.prepare_key(sshrsaSecret)
options={'verify_exp': False,  # Skipping expiration date check
         'verify_aud': False } # Skipping audience check
print jwt.decode(access_token, key=shakey, options=options)

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 jwt

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

# Load the key we created
with 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 place
data = {'user_id': 1}

# Lets create the JWT token -- this is a byte array, meant to be sent as an HTTP header
jwt_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 valid
print(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 successfuly
with 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 exception
print(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

Usage Examples — PyJWT 1.7.1 documentation, For example, in situations where the token issuer uses multiple keys and you have no of the issuer's public keys or shared secrets to use for validation, the issuer may Expiration time is automatically verified in jwt.decode() and raises jwt. Regarding "Using Public-Key Signatures with JWTs" , if i have multiple consumers for my api and every consumer signs token with their own private key , and i do have public keys of all consumers . when anyone of api consumers sends token how would i know which public key to use to decode it .

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

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

Welcome to PyJWT — PyJWT 1.7.1 documentation, In their most common format, a "secret key" is used in the generation and me first show you how to work with them using Python with the pyjwt package. In addition to user information, the payload of a JWT can include a few fields Anybody interested in verifying the message can use my public key to� Command line. Usage: pyjwt [options] INPUT Decoding examples: pyjwt --key=secret decode TOKEN pyjwt decode --no-verify TOKEN See more options executing pyjwt --help.

@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 jwt
data = {
    "sample" : "data"
}

# Encode data
with open("auth.pem") as key_file:
    token = jwt.encode(data, key=key_file.read(), algorithm='RS256')

print(token)

# Decode data with only he public key
with 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'}

JSON Web Tokens with Public Key Signatures, A JSON Web Key (JWK) is a JSON object representing a public key. You can use one to verify a JWT issued by an OIDC provider signing its� JWT authentication policy for Pyramid. JWT authentication for Pyramid. This package implements an authentication policy for Pyramid that using JSON Web Tokens.This standard is often used to secure backend APIs.

You can use pyjwkest to extract the token and verify:

pip install pyjwkest

_decode_token will verify whether the signature matches with the content in the token but it will not verify things like expiration date, token issuer etc.

_validate_claims will check issuer and expiration dates.

Most of the code is from here: https://github.com/ByteInternet/drf-oidc-auth/blob/master/oidc_auth/authentication.py with a little bit of simplification.

import datetime
import logging
from calendar import timegm
from typing import Dict

import requests
from jwkest import JWKESTException
from jwkest.jwk import KEYS


class TokenChecker():
    def __init__(self):
        self.config_url: str = 'https://{your-oidc-provider}/auth/realms/{your-realm}/.well-known/openid-configuration/'
        self._load_config()
        self._load_jwks_data()

    def _load_config(self):
        # Loads issuer and jwks url (see method below)
        self.oidc_config: Dict = requests.get(self.config_url, verify=True).json()
        self.issuer = self.oidc_config['issuer']

    def _load_jwks_data(self):
        # jwks data contains the key you need to extract the token
        self.jwks_keys: KEYS = KEYS()
        self.jwks_keys.load_from_url(self.oidc_config['jwks_uri'])

    def _decode_token(self, token: str):
        try:
            self.id_token = JWS().verify_compact(token, keys=self.jwks_keys)
        except JWKESTException:
            logging.error('Invalid Authorization header. JWT Signature verification failed')

    def _validate_claims(self):
        if self.id_token.get('iss') != self.issuer:
            msg = 'Invalid Authorization header. Invalid JWT issuer.'
            logging.error(msg)

        # Check if token is expired
        utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple())
        if utc_timestamp > self.id_token.get('exp', 0):
            msg = 'Invalid Authorization header. JWT has expired.'
            logging.error(msg)
        if 'nbf' in self.id_token and utc_timestamp < self.id_token['nbf']:
            msg = 'Invalid Authorization header. JWT not yet valid.'
            logging.error(msg)

    def check_token(self, token: str):
        self._decode_token(token=token)
        self._validate_claims()

Now check your token with:

if __name__ == '__main__':
    TokenChecker().check_token(token='your-jwt-token')

Verifying JWTs with JWKs and PyJWT, This is a guide to using pyjwt to sign and validate a JWT using RS256. Here is how use the OpenSSL command line tool to generate a private key with a size� After extracting the public key we can finally validate the JWT access token. jwt.decode( access_token, public_key, algorithms='RS256', audience=app_id, ) If the JWT token is successfully decoded and validated by the decode () function we ensured that the token is valid and can be trusted.

JWTs signed with RS256 in Python: A demonstration of org-babel , PyJWT is a Python library which allows you to encode and decode JSON If you want to quickly add secure token-based authentication to Python projects, feel free to check mentations for RSA with SHA256 and EC with SHA256 signatures. >>decoded = jwt.decode(encoded, key, algorithms='HS256'). Now that our actual code is ready, let’s test it out. I recommend using postman for testing out the APIs. You can use something like CURL but I will be using postman for this tutorial. To start testing our api, first we need to run our API. To do so, open up a terminal window and type the following in it. python app.py. You should see an

[PDF] PyJWT Documentation, I started out using python-jose but that has lead me down a bit of a rabbit jwt, jwt-validation, python Trying to extract the public key has been a bit of an adventure and I I'd like to think it is as easy as verify(access_token, jwks_json) . in /.well-known/jwks.json is x.509. pyjwt requires the public key,� Reading Headers without Validation¶. Some APIs require you to read a JWT header without validation. For example, in situations where the token issuer uses multiple keys and you have no way of knowing in advance which one of the issuer’s public keys or shared secrets to use for validation, the issuer may include an identifier for the key in the header.

Token validation with python, JSON Web Token implementation in Python. pip install PyJWT==1.4.0 jwt. decode(encoded, verify=False) {u'some': u'payload'} The type of key (private or public) depends on whether you are signing or verifying. So, the question is how exactly in C# can I verify this JWT using the public key for the RS256 algorithm I've got? It would be awesome if there is a good tutorial describing this procedure explicitly. However, an example of how to do this using System.IdentityModel.Tokens.Jwt will also work fine.

Comments
  • Excellent work in solving your own issue. For future reference, you're encouraged to answer your own question rather than editing the question field stackoverflow.com/help/self-answer
  • In update 2, it seems that the decode that works is when using testtoken, but in the one that does not work, it is the access_token trying to be decoded.
  • Yes, we have to use json.dumps(jwk) as a decode.