Decode Firebase JWT in Python using PyJWT - python

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")

Related

Python JWT doesn't validate with google public key

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"

Problem decoding JWT token with public key from Gravitee in Python

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?

Coinlist exchange API authentication - Python

For the past month I've been trying to understand coinlist API (https://coinlist.co) since there is no API wrapper available it's been a hard task. I could figure it out that their API docs are really similar to coinbase exchange, and tried to extrapolate but with no success.
import json, hmac, hashlib, time, requests
from requests.auth import AuthBase
# Before implementation, set environmental variables with the names API_KEY and API_SECRET
API_KEY = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
API_SECRET = 'xxxx/xxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxx=='
# Create custom authentication for Coinlist API
class CoinlistWalletAuth(AuthBase):
def __init__(self, api_key, secret_key):
self.api_key = api_key
self.secret_key = secret_key
def __call__(self, request):
timestamp = str(int(time.time()))
message = timestamp + request.method + request.path_url + (request.body or '')
signature = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
request.headers.update({
'CL-ACCESS-SIGN': signature,
'CL-ACCESS-TIMESTAMP': timestamp,
'CL-ACCESS-KEY': self.api_key,
})
return request
auth = CoinlistWalletAuth(API_KEY, API_SECRET)
#Test1 - Fetching account balance
response = requests.get('https://trade-api.coinlist.co/v1/accounts', auth=auth)
I am getting this TypeError: key: expected bytes or bytearray, but got 'str' when calling for the response.
Docs say - You must base64-encode the signature (the output of the sha256 HMAC). Why is it failing?
Two things:
TypeError: key: expected bytes or bytearray, but got 'str'
hmac.new(key, msg=None, digestmod='')
Return a new hmac object. key is a bytes or bytearray object giving the secret key. (...)
import base64
...
secret_key_as_bytes = base64.b64decode(self.secret_key, altchars=None)
The CL-ACCESS-SIG header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp + HTTP method + path + body (where '+' represents string concatenation) and base64-encode the output.
digest = hmac.new(secret_key_as_bytes, message, hashlib.sha256).digest()
signature = str(base64.b64encode(digest))
wish it's not too late
hmac.new()
function need the key argument to be bytes,so:
byte_key = bytes(self.secret_key, 'UTF-8') # key.encode() would also work in this case
but it's no done yet, if you go with this, you would get
TypeError: Unicode-objects must be encoded before hashing
to fix this , your message should be encoded.
message = message.encode()
h = hmac.new(byte_key, message, hashlib.sha256).hexdigest()
now it's done.
Howerver I got 400. sad.
I found someone have done this:
https://gist.github.com/badmofo/d109fbc447fea64d99e5ca58bdf53d7b

How to do HMAC-SHA512 signing in Python? [duplicate]

This question already has an answer here:
How do I sign a POST request using HMAC-SHA512 and the Python requests library?
(1 answer)
Closed 5 years ago.
I want to do a get request in the following format:
https://bittrex.com/api/v1.1/account/getbalance?apikey=API_KEY&currency=BTC
I do have the public and the secret key. However I found the following statement:
For this version, we use a standard HMAC-SHA512 signing. Append apikey and nonce to your request and calculate the HMAC hash and include it under an apisign header
I don't really know how to properly encrypt my key. Using the plain secret key obviously returns "NONCE_NOT_PROVIDED". Everything I got is this:
current_price = requests.get("https://bittrex.com/api/v1.1/account/getbalance?apikey=API_KEY&currency=BTC")
How to properly sign and encrypt the keys? Thank you.
Edit:
Current attempt looks like the following.
def getWalletSize():
APIkey = b'29i52wp4'
secret = b'10k84a9e'
s = "https://bittrex.com/api/v1.1/account/getbalance?apikey=29i52wp4&currency=BTC"
digest = hmac.new(secret, msg=s, digestmod=hashlib.sha512).digest()
current_balance = requests.get(digest)
return current_balance
However it raises the error Unicode-objects must be encoded before hashing
import hmac
import hashlib
import base64
API_KEY = 'public_key'
s = """GET https://bittrex.com/api/v1.1/account/getbalance?apikey=%s&currency=BTC""" % API_KEY
base64.b64encode(hmac.new("1234567890", msg=s, digestmod=hashlib.sha512).digest())
it signs the request
digest = hmac.new(secret_key, msg=thing_to_hash, digestmod=hashlib.sha512).digest()
and this encode it in base64
base64.b64encode(digest)

Amazon SNS with Python

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.

Categories