I'm trying to decode a Gravitee JWT Token using public key. I tested already PyJWT, authlib, python-jose and jwcrypto libraries and review a lot of posts on this page but I get the same error in all of them and I could not fix the problem.
Error:
('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [_OpenSSLErrorWithText(code=151584876, lib=9, reason=108, reason_text=b'error:0909006C:PEM routines:get_name:no start line')])
Firts of all I get the public key following Gravitee instructions:
https://docs.gravitee.io/am/current/am_userguide_create_certificate.html
Some info from https://jwt.io about my token:
HEADER:ALGORITHM & TOKEN TYPE
{
"kid": "default",
"alg": "RS256"
}
Python packeges versions:
PyJWT==2.3.0 (also tested with 2.1.0)
cryptography==36.0.0 (some posts suggests is required)
My code:
from rest_framework import permissions
from rest_framework.exceptions import APIException
from django.conf import settings
import jwt
class TokenNotValid(APIException):
status_code = 403
default_detail = "Invalid or absent JWT token field."
class NoAuthHeader(APIException):
status_code = 403
default_detail = "Absent 'Authorization' header."
class ValidJWTPermission(permissions.BasePermission):
"""
Global permission check for JWT token.
"""
def _get_pubkey(self):
key = """-----BEGIN PUBLIC KEY-----\n""" + settings.GRAVITEE_PUBLIC_KEY + """\n-----END PUBLIC KEY-----"""
return key
def has_permission(self, request, view):
auth_header = request.META.get('HTTP_AUTHORIZATION')
# print("Received header:")
# print(auth_header)
if auth_header is None:
raise NoAuthHeader
try:
token = auth_header.split()[1]
# print("Encoded Token:")
# print(token)
public_key = self._get_pubkey()
print(public_key)
claims = jwt.decode(token, key=public_key, algorithms=['RS256'])
claims.validate()
except Exception as e:
print(e)
raise TokenNotValid
# print("Decoded token:")
# print(dec_token)
return True
I tested also encoding the key like key.encode() and key.encode('ascii') or composing the key with "BEGIN RSA PUBLIC KEY" instead of "BEGIN PUBLIC KEY" and anything works for me. Always I have the same error.
Depending on the library, you often wants the key in JWK format as shown in this link:
https://demo.identityserver.io/.well-known/openid-configuration/jwks
This JSON Web Key (JWK) format is a standard for representing keys when you deal with tokens and perhaps you library wants it in that format too?
Related
I am using Auth0 in my DRF project to generate tokens and authenticate. Everything works fine if I normally encode and decode the tokens. But I have written a method requires_scope which determines if the required scope (which the decorator on API tells) for any API. The method is below:
def requires_scope(required_scope):
"""Determines if the required scope is present in the Access Token
Args:
required_scope (str): The scope required to access the resource
"""
def require_scope(f):
#wraps(f)
def decorated(*args, **kwargs):
token = get_token_auth_header(args[0])
decoded = jwt.decode(token, verify=False, algorithms=settings.AUTH0_ALGORITHMS)
if decoded.get("scope"):
token_scopes = decoded["scope"].split()
for token_scope in token_scopes:
if token_scope == required_scope:
return f(*args, **kwargs)
response = JsonResponse({'message': 'You don\'t have access to this resource'})
response.status_code = 403
return response
return decorated
return require_scope
now when I use the decorator on API for any specific scope, it does not decode the JWT and shows the following ValueError error:
('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [_OpenSSLErrorWithText(code=75497580, lib=9, reason=108, reason_text=b'error:0480006C:PEM routines::no start line')])
This is my decoding method:
def jwt_decode_token(token):
header = jwt.get_unverified_header(token)
jwks = requests.get('https://{}/.well-known/jwks.json'.format(settings.SOCIAL_AUTH_AUTH0_DOMAIN)).json()
public_key = None
for jwk in jwks['keys']:
if jwk['kid'] == header['kid']:
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk))
# public_key = "-----BEGIN PUBLIC KEY-----\n" + jwk['x5c'][0] + "\n-----END PUBLIC KEY-----\n"
# print(public_key)
if public_key is None:
raise Exception('Public key not found.')
issuer = 'https://{}/'.format(settings.SOCIAL_AUTH_AUTH0_DOMAIN)
return jwt.decode(
token,
public_key,
audience=settings.AUTH0_TOKEN_API_AUDIENCE,
issuer=issuer,
algorithms=['RS256']
)
and this is the payload I am passing to API Call (it contains the scope):
payload = f"{{\"client_id\":\"{settings.SOCIAL_AUTH_AUTH0_KEY}\",\
\"client_secret\":\"{settings.SOCIAL_AUTH_AUTH0_SECRET}\",\
\"audience\":\"{settings.AUTH0_TOKEN_API_AUDIENCE}\",\
\"grant_type\":\"password\",\
\"username\":\"{email}\",\
\"password\":\"{password}\",\
\"scope\":\"read:messages\"}}"
What's wrong I am doing? Can anyone help?
The reason why it complains about the key is because it is still trying to do the verification. In order to correctly disable this with pyjwt is:
jwt.decode(token, algorithms=["RS256"], options={"verify_signature": False})
See the pyjwt documentation (interestingly, I cannot skip the algorithms block): https://pyjwt.readthedocs.io/en/latest/usage.html#reading-the-claimset-without-validation
Tried to decode the JWT provided by the google. I have used phone number authentication with google and returned verifyIdToken which is a jwt token. I have tried to verify with the jwt package available in python but it throws error.
When I tried to verify the signature with jwt.io It give a verified signature but it python can't.
Here is the link about the documentation of the google
The public key used is
-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIICc/DAoum8fgwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMjIw\nNTAzMDkzODM5WhcNMjIwNTE5MjE1MzM5WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAK7kUdLrSbHeVqsGd2KC2Kt4Bup69/+2cXmfGALrYFGsKdkE\nUdeV8Mdtbqtk5njUAzXibrZb+x3jfyG/WJZXbFXgBPkSHIsIcwFFnIMQMHRfXXwV\nq1Qe5U52x2ztSYGPtz3UNBUUXsHZLplGdljtjagDqNYX1vYA6ZXItQPr1ycM0i1f\nV7j96qQ0OJjir94B1j5cTVHHtZsqoJgcJXdDabF5zC6G1X3Gxh3OftJBqM0dqWjl\nLmgGQ6CtYRmB10zqotJNa3v9Q6jT0flNpLOswnEW8t44/sjRl3sf2Tv3IMrihZYB\n2CaEvL1b9DAvWfAucrG7x8BKBKTs5u2bowqPKlcCAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBAJpMc+noeLGaKXlBrW30vEnp9KjN+bhf4WipBSV4KlQx\nwxYnZov6hrxhctz0D0rhQmIHNlmgIFkG/ej9sszz4z08MZAxrPUJsaGoIY2e4PQ3\nYOzNHFp4VssK9D1L5jxRxf+/jf0fJf/ZfFKxCzz8tWfpZitHlzmOoSMuUBTIFXmB\nvQuUWeOu3pAT7Z+ddpaLvxbE264Ybd9ujxuFWzmXJCNHh+dOruymEZrpwoOkoAaH\nll7Jr2nEaY/SUCK4QZne3FXIc07rbs9l4C8+yrcG5RkAsVH9gdgtHDgFWLFnxGb8\nQQ8yX4+6Fq/3xWzUWg4PvAIb4aX8Fwc59uBFc7yeSvg=\n-----END CERTIFICATE-----\n
I tried removing \n and I verified it with jwt.io it worked but it doesn't work in the python the code I wrote is
import jwt
key = "-----BEGIN CERTIFICATE-----MIIDHDCCAgSgAwIBAgIICc/DAoum8fgwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UEAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMjIwNTAzMDkzODM5WhcNMjIwNTE5MjE1MzM5WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tlbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7kUdLrSbHeVqsGd2KC2Kt4Bup69/+2cXmfGALrYFGsKdkEUdeV8Mdtbqtk5njUAzXibrZb+x3jfyG/WJZXbFXgBPkSHIsIcwFFnIMQMHRfXXwVq1Qe5U52x2ztSYGPtz3UNBUUXsHZLplGdljtjagDqNYX1vYA6ZXItQPr1ycM0i1fV7j96qQ0OJjir94B1j5cTVHHtZsqoJgcJXdDabF5zC6G1X3Gxh3OftJBqM0dqWjlLmgGQ6CtYRmB10zqotJNa3v9Q6jT0flNpLOswnEW8t44/sjRl3sf2Tv3IMrihZYB2CaEvL1b9DAvWfAucrG7x8BKBKTs5u2bowqPKlcCAwEAAaM4MDYwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADggEBAJpMc+noeLGaKXlBrW30vEnp9KjN+bhf4WipBSV4KlQxwxYnZov6hrxhctz0D0rhQmIHNlmgIFkG/ej9sszz4z08MZAxrPUJsaGoIY2e4PQ3YOzNHFp4VssK9D1L5jxRxf+/jf0fJf/ZfFKxCzz8tWfpZitHlzmOoSMuUBTIFXmBvQuUWeOu3pAT7Z+ddpaLvxbE264Ybd9ujxuFWzmXJCNHh+dOruymEZrpwoOkoAaHll7Jr2nEaY/SUCK4QZne3FXIc07rbs9l4C8+yrcG5RkAsVH9gdgtHDgFWLFnxGb8QQ8yX4+6Fq/3xWzUWg4PvAIb4aX8Fwc59uBFc7yeSvg=-----END CERTIFICATE-----"
token = "XXXXXXXXX"
payload = jwt.decode(token, key.encode(), algorithms=['RS256',])
print(payload)
the error that throwed is
raise ValueError(
ValueError: ('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [_OpenSSLErrorWithText(code=75497580, lib=9, reason=108, reason_text=b'error:0480006C:PEM routines::no start line')])
I haven't included the token because it contains data.
I was able to reproduce the following:
import jwt
key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----\n"
token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"
payload = jwt.decode(token, key.encode(), algorithms=['RS256',])
print(payload)
>>> {'sub': '1234567890', 'name': 'John Doe', 'admin': True, 'iat': 1516239022}
I noticed that in your public key there are some escape characters \n inside your string (this may be causing your problem). You should only remove \n that appear inside the key and include them at the beginning and at the end. Try changing your public key to:
key = "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIICc/DAoum8fgwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UEAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMjIwNTAzMDkzODM5WhcNMjIwNTE5MjE1MzM5WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tlbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7kUdLrSbHeVqsGd2KC2Kt4Bup69/+2cXmfGALrYFGsKdkEUdeV8Mdtbqtk5njUAzXibrZb+x3jfyG/WJZXbFXgBPkSHIsIcwFFnIMQMHRfXXwVq1Qe5U52x2ztSYGPtz3UNBUUXsHZLplGdljtjagDqNYX1vYA6ZXItQPr1ycM0i1fV7j96qQ0OJjir94B1j5cTVHHtZsqoJgcJXdDabF5zC6G1X3Gxh3OftJBqM0dqWjlLmgGQ6CtYRmB10zqotJNa3v9Q6jT0flNpLOswnEW8t44/sjRl3sf2Tv3IMrihZYB2CaEvL1b9DAvWfAucrG7x8BKBKTs5u2bowqPKlcCAwEAAaM4MDYwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADggEBAJpMc+noeLGaKXlBrW30vEnp9KjN+bhf4WipBSV4KlQxwxYnZov6hrxhctz0D0rhQmIHNlmgIFkG/ej9sszz4z08MZAxrPUJsaGoIY2e4PQ3YOzNHFp4VssK9D1L5jxRxf+/jf0fJf/ZfFKxCzz8tWfpZitHlzmOoSMuUBTIFXmBvQuUWeOu3pAT7Z+ddpaLvxbE264Ybd9ujxuFWzmXJCNHh+dOruymEZrpwoOkoAaHll7Jr2nEaY/SUCK4QZne3FXIc07rbs9l4C8+yrcG5RkAsVH9gdgtHDgFWLFnxGb8QQ8yX4+6Fq/3xWzUWg4PvAIb4aX8Fwc59uBFc7yeSvg=\n-----END CERTIFICATE-----\n"
I am trying to verify an idToken using a public key in python.
I first convert the JWK token to PEM but when I call the "decode" function, I see a "signature verification failed" exception. What am I missing?
# Long string goes here - this is the token to verify
myToken = 'ezFraWQiXXX.YYYYYYYY.ZZZZZZZZ'
# JWK Token
webkey = {
"alg": "RS256",
"e": "AQAB",
"kid": "d9FzOfniXuHf2sF3opIKZb0sW8Nuaa0d5d+AXXXXXXXX=",
"kty": "RSA",
"n": "nQwBvRlZKdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4HcenyO_WASyjr6korLEHxh8XXXXXXXXXXXX",
"use": "sig"
}
# Converting JWK to PEM
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(webkey)
pubk_bytes = public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo)
# This is where I get the "signature verification failed" exception
claim = jwt.decode(myToken, pubk_bytes, algorithms=['RS256']) # <<-- ideally this should decode the token for me
Looks like you are using PyJWT, which is a good choice of security library. Ideally get the library to download token signing public keys for you from the JWKS endpoint of the Authorization Server, for the cleanest and simplest code:
import jwt
from jwt import PyJWKClient
// The client will read the JWT header to get the kid field,
// then download token signing public keys and return that matching the kid.
// This key will then be cached for future JWTs with the same kid.
// The client will reliably handle new kids if keys are recycled.
jwks_client = PyJWKClient(url)
signing_key = jwks_client.get_signing_key_from_jwt(access_token_jwt)
// It is recommended to verify the signature, expiry, issuer and audience.
// As a best practice, the API should also specify the algorithms it expects
// to receive in JWT signatures.
claims = jwt.decode(
access_token_jwt,
signing_key.key,
algorithms=["RS256"],
issuer="my-issuer",
audience="my-audience")
You should be able to send a JWK directly into the library, rather than dealing with RSA or byte translation. See the pyJWT docs for some examples. For further security background, you may also find the Curity article on JWT Best Practices useful.
I have written the following code :
def check_token(token):
response = requests.get("https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com")
key_list = response.json()
decoded_token = jwt.decode(token, key=key_list, algorithms=["RS256"])
print(f"Decoded token : {decoded_token}")
I am trying to decode the token provided by firebase client side to verify it server-side.
The above code is throwing the following exception :
TypeError: Expecting a PEM-formatted key.
I have tried to not pass a list to the jwt.decode method, only the key content and i have a bigger error that the library could not deserialize the Key.
I was following this answer but i am getting this error.
Is it a requests conversion problem ? What am i doing wrong ?
The 2nd parameter key in decode() seems to take a string value instead of list. The Google API request returns a dict/map containing multiple keys. The flow goes like:
Fetch public keys from the Google API endpoint
Then read headers without validation to get the kid claim then use it to get appropriate key from that dict
That is a X.509 Certificate and not the public key as in this answer so you need to get public key from that.
The following function worked for me:
import jwt
import requests
from cryptography.hazmat.backends import default_backend
from cryptography import x509
def check_token(token):
n_decoded = jwt.get_unverified_header(token)
kid_claim = n_decoded["kid"]
response = requests.get("https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com")
x509_key = response.json()[kid_claim]
key = x509.load_pem_x509_certificate(x509_key.encode('utf-8'), backend=default_backend())
public_key = key.public_key()
decoded_token = jwt.decode(token, public_key, ["RS256"], options=None, audience="<FIREBASE_PROJECT_ID>")
print(f"Decoded token : {decoded_token}")
check_token("FIREBASE_ID_TOKEN")
Trying to get started with Amazon SNS. API is very simple REST. I seem to be hung up on the signature part of the call. The API example is:
http://sns.us-east-1.amazonaws.com/
?Subject=My%20first%20message
&TopicArn=arn%3Aaws%3Asns%3Aus-east-1%3A698519295917%3AMy-Topic
&Message=Hello%20world%21
&Action=Publish
&SignatureVersion=2
&SignatureMethod=HmacSHA256
&Timestamp=2010-03-31T12%3A00%3A00.000Z
&AWSAccessKeyId=AKIAJHS4T6XPF7XIURNA
&Signature=9GZysQ4Jpnz%2BHklqM7VFTvEcjR2LIUtn6jW47054xxE%3D
I've been following the API docs for signatures, so I'm trying:
from time import strftime,gmtime,time
import urllib2
import hmac
import hashlib
import base64
import string
def publichSNSMsg(Subject,TopicArn,Message,AWSAccessKeyId,privatekey):
#http://docs.amazonwebservices.com/AWSSimpleQueueService/2008-01-01/SQSDeveloperGuide/
amzsnshost = 'sns.us-east-1.amazonaws.com'
values = {'Subject' : Subject,
'TopicArn' : TopicArn,
'Message' :Message,
'Timestamp' : strftime("%Y-%m-%dT%H:%M:%S.000Z", gmtime(time())),
'AWSAccessKeyId' : AWSAccessKeyId,
'Action' : 'Publish',
'SignatureVersion' : '2',
'SignatureMethod' : 'HmacSHA256',
}
amazquote=lambda v: urllib2.quote(v).replace('%7E','~')
cannqs=string.join(["%s=%s"%(amazquote(key),amazquote(values[key])) for key in sorted(values.keys(),key=str.lower)],'&')
string_to_sign=string.join(["GET",amzsnshost,"/",cannqs],'\n')
sig=base64.encodestring(hmac.new(privatekey,string_to_sign,hashlib.sha1).digest())
querystring = "%s&Signature=%s"%(cannqs,amazquote(sig))
url="http://%s/?%s"%(amzsnshost,querystring)
try:
return urllib2.urlopen(url).read()
except urllib2.HTTPError, exception:
return "Error %s (%s):\n%s"%(exception.code,exception.msg,exception.read())
And getting back:
<ErrorResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
</Error>
<RequestId>8d6e5a41-dafb-11df-ac33-f981dc4e6c50</RequestId>
</ErrorResponse>
Any ideas?
Aw, it was simple!
Keys in the query string were supposed to be byte-order sorted, not case-insensitive sorted (that was for version 1 signatures).
Slightly updated (and now correct) code is available on gist.
I found this example so useful that I rewrote it in C#. Since AWS does not have a WP7 library for SNS, perhaps this will be of help to someone: https://gist.github.com/2705156
I would suggest you to use boto3 for SNS. The documentation can be found at http://boto3.readthedocs.io/en/latest/reference/services/sns.html. The following code snippet can be used for fetching signature as JSON.
import boto3
from django.views.decorators.csrf import csrf_exempt
topic_arn = <your topic>
client = boto3.client('sns',region_name="us-west-2")
#Call this to publish
def sns_publish(arg1,arg2):
response = client.publish(
TopicArn=topic_arn,
Message=arg2,
Subject=arg2
)
#Function to subscribe to topic
#csrf_exempt
def sns_parse(request):
print request.body.decode('utf-8')
if request.method == "POST":
response = json.loads(request.body.decode('utf-8'))
type = response.get('Type')
if type == 'Subscription':
print(type)
return("Subscription URL: "+<Url obtained>)
elif type == 'Notification':
return HttpResponse(type.get('Signature'))
else:
return HttpResponse('Not a POST object')
This is just a template code though, but yeah, boto3 is indeed helpful.