Lee
Lee

Reputation: 2730

How to verify a JWT using python PyJWT with a public PEM cert?

I recently upgraded from PyJWT 0.4.1 to 1.0.1 and I can't figure out how to verify a JWT signed with a public key.

My code looks like this:

import jwt
cert_string = "-----BEGIN CERTIFICATE-----\nMIICITCCAYqgAwIBAgIIBEsUSxL..."
token_string = "eyJhbGciOiJSUzI1NiIsImtpZCI6I..."
jwt.decode(token_string, cert_string, algorithms=['RS256'])

The error I get is:

File "<stdin>", line 1, in <module>
File "~/.virtualenvs/project/lib/python2.7/site-packages/jwt/api.py", line 117, in decode                            
key, algorithms, **kwargs)                                                                                         
File "~/.virtualenvs/project/lib/python2.7/site-packages/jwt/api.py", line 176, in _verify_signature                 
key = alg_obj.prepare_key(key)                                                                                     
File "~/.virtualenvs/project/lib/python2.7/site-packages/jwt/algorithms.py", line 165, in prepare_key                
key = load_pem_public_key(key, backend=default_backend())                                                          
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/primitives/serialization.py", line 24, in load_pem_public_key
return backend.load_pem_public_key(data)                                                                           
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/backends/multibackend.py", line 285, in load_pem_public_key
return b.load_pem_public_key(data)
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 716, in load_pem_public_key
self._handle_key_loading_error()
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 912, in _handle_key_loading_error
raise ValueError("Could not unserialize key data.")                                                                
ValueError: Could not unserialize key data.

I'm confident my cert_string and token are good. The following code runs OK:

from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend
load_pem_x509_certificate(cert_string, default_backend())

My code that used to work with 0.4.1 looked like this:

cert_string = "".join(cert_string.strip().split("\n")[1:-1])
der = a2b_base64(cert_string)
cert = DerSequence()
cert.decode(der)
tbsCertificate = DerSequence()
tbsCertificate.decode(cert[0])
subjectPublicKeyInfo = tbsCertificate[6]
pub_key = RSA.importKey(subjectPublicKeyInfo)
jwt.decode(token_string, pub_key)

Any help would be appreciated.

Upvotes: 11

Views: 23516

Answers (3)

Nagalakshmi Srirama
Nagalakshmi Srirama

Reputation: 1021

I am using python 3.6

import jwt
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
key = b"""-----BEGIN PUBLIC KEY-----
MIIBI....<<Your public key basically>>
-----END PUBLIC KEY-----"""
public_key = serialization.load_pem_public_key(key, backend=default_backend())
print(jwt.decode(token, public_key))

This worked like a charm for me.

Upvotes: 1

Ryu_hayabusa
Ryu_hayabusa

Reputation: 3722

If the key file is generated using ssh-keygen -t rsa (RFC4716), you can use the file directly.

Encode:

import jwt

pemfile = open("id_rsa", 'r')
keystring = pemfile.read()
pemfile.close()
token = jwt.encode(payload, keystring, algorithm='RS256')

Decode:

import jwt

pemfile = open("id_rsa.pub", 'r')
keystring = pemfile.read()
pemfile.close()
payload = jwt.decode(toekn, keystring,  verify=True)

dont forget to catch errors like jwt.ExpiredSignatureError etc

Upvotes: 3

Hans Z.
Hans Z.

Reputation: 53918

You need to pass the public key instead of the full certificate to the decode method. So extract the key from the certificate in order to use it as in:

from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

cert_str = "-----BEGIN CERTIFICATE-----MIIDETCCAfm..."
cert_obj = load_pem_x509_certificate(cert_str, default_backend())
public_key = cert_obj.public_key()

and then:

token_string = "eyJhbGciOiJSUzI1NiIsImtpZCI6I..."
jwt.decode(token_string, public_key, algorithms=['RS256'])

Upvotes: 21

Related Questions