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)