5

I have received a JWT token. I'm interested in unpacking it and validating it's contents.

I want to use the pyJWT library to do it. See the small program below.

I have base64 decoded my JWT successfully. And I have retrieved the matching JWK from the web. But I don't know what to do next to verify the signature.

When I try to do the jwt.decode() below I get the error jwt.exceptions.DecodeError: Not enough segments.

# !/usr/bin/env python

import jwt
import base64
import json
import requests

my_jwt = "eyJqa3UiOiJodHRwczovL2U5N2I4YTlkNjcyZTRjZTQ4NDVlYzY5NDdjZDY2ZW" \
         "Y2LXNiLmJhYXMubmludGVuZG8uY29tLzEuMC4wL2NlcnRpZmljYXRlcyIsImtp" \
         "ZCI6ImZlOWRiYmZmLTQ3MGItNDZjOC04YmFmLTFiNzY5OGRlZTViZSIsImFsZy" \
         "I6IlJTMjU2In0.eyJpc3MiOiJodHRwczovL2U5N2I4YTlkNjcyZTRjZTQ4NDVl" \
         "YzY5NDdjZDY2ZWY2LXNiLmJhYXMubmludGVuZG8uY29tIiwiZXhwIjoxNTQ1MT" \
         "g1NDk2LCJ0eXAiOiJpZF90b2tlbiIsImF1ZCI6IjhkOTc1NTllNjNlY2NkNTYi" \
         "LCJiczpkaWQiOiI2NjJhZTQwOWYwNTQyYTBjIiwic3ViIjoiOTNkYmYwNDdiYT" \
         "I3NzQ5NSIsImp0aSI6IjY1NDg4ZjJmLTI1NzAtNDBkYy04ODQ3LTMzODNlZWIx" \
         "MGJiYiIsIm5pbnRlbmRvIjp7ImFpIjoiMDEwMGY4MDAwMDQ5MjAwMCIsImF2Ij" \
         "oiMDAwMCIsImVkaSI6ImJjNTdiYmM3MTZlMDA1MGFmOWRhN2NkYTIzMWRjZDgy" \
         "IiwiYXQiOjE1NDUxNzQ2OTZ9LCJpYXQiOjE1NDUxNzQ2OTZ9.ZMUIt3wYrbfhX" \
         "nnDh4WraGlKrZy0YuL5prluY70sU_-0W5XvWIB-xmTrLz7LJWHEGwTskcWf81_" \
         "HBq_mSb75rMfTAEBwBmOJ4ITmhdnXksz8w7EDOWuPPSEft5XLMNOMD16ztEOYe" \
         "5ddU_iqNEbT56L7fcAJEXv0FWy6H_OutxOglYpDaNkcj6CWJ7dpA0JbqerR9dE" \
         "szaLwyn1ZBDPVD0YeAIm5bEr61imeedzMb0amxlTl4R87mqK6epsFUnRy6p6Kl" \
         "r27_DlTLQ-gej09W7NeNzONCj4thHgCr9szAiaN28krfTc2fobz3qFCoC_eQgh" \
         "iIIZBe_-Lksng3Eg6tw"

decoded_token = [
    base64.b64decode(i + '=' * (-len(i) % 4))
    for i in my_jwt.split('.')
]
header = json.loads(decoded_token[0])
claims = json.loads(decoded_token[1])
signature = decoded_token[2]

print 'Header = \n{}\n\n'.format(header)
print 'Claims = \n{}\n\n'.format(claims)
print 'Signature = \n{}\n\n'.format(signature)

key_found = False
for curr_jwk in requests.get(header['jku']).json()['keys']:
    if curr_jwk['kid'] == header['kid']:
        key_found = True
        break
assert key_found
print 'curr_jwk = {}\n\n'.format(curr_jwk)

# I am not sure if the following line is correct
jwt.decode(signature, curr_jwk['x5c'][0], algorithms=['RS256'])

Below is the full output. Please show me how I can verify the signature. I haven't found any python examples out there on the web that worked for me.

Header =
{u'jku': u'https://e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com/1.0.0/certificates', u'alg': u'RS256', u'kid': u'fe9dbbff-470b-46c8-8baf-1b7698dee5be'}


Claims =
{u'aud': u'8d97559e63eccd56', u'iss': u'https://e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com', u'jti': u'65488f2f-2570-40dc-8847-3383eeb10bbb', u'exp': 1545185496, u'nintendo': {u'ai': u'0100f80000492000', u'edi': u'bc57bbc716e0050af9da7cda231dcd82', u'at': 1545174696, u'av': u'0000'}, u'iat': 1545174696, u'bs:did': u'662ae409f0542a0c', u'typ': u'id_token', u'sub': u'93dbf047ba277495'}


Signature =
�dr>�X�ݤn��G�D�6��)�d�T=x&�+�X�y�s1��S��|�j���lI�˪z*Z��9S�OV�׍��B��a������v�J�M͟���P�m>z/� �d�����H:�)


curr_jwk = {
    u'use': u'sig', 
    u'e': u'AQAB', 
    u'kty': u'RSA', 
    u'alg': u'RS256', 
    u'n': u'twZRFUFinbzSF9aCaLqSdDSfS4Lfz6bSB-4ymwqkgJPLMLtX0meEVOSsx2qP_OQFkb6_RAZk3GK2CSM46V8A-bZqYn1jHyRyQeQmAfrzvXxAiXBdSq4Eso1C_roFQDjR3p-RnLWDLdMPUHkmAQSPEpVAUbcCvUD8zZwQ-go8GAzG0iQNNvZxma1JQoDsHPgY5LiBVBlqY43tp_jeW_sb3SxZ30zzIckKtQfYDfph_vns-q1_raNRPgS0TQw4E470n27UgLE30yoVn_DlYwmtZneCOWIByVPrMBMIkrv_Gh9VW3GszbqZz2u3K9Aj7rtnCtmUrI9dQbeTnruWOn_Fjw', 
    u'x5c': [u'MIIC/zCCAeegAwIBAgIHBX0u6g3YCzANBgkqhkiG9w0BAQUFADBAMT4wPAYDVQQDEzVlOTdiOGE5ZDY3MmU0Y2U0ODQ1ZWM2OTQ3Y2Q2NmVmNi1zYi5iYWFzLm5pbnRlbmRvLmNvbTAeFw0xNzEyMTYwMDAwMDBaFw0xOTEyMTcwMDAwMDBaMEAxPjA8BgNVBAMTNWU5N2I4YTlkNjcyZTRjZTQ4NDVlYzY5NDdjZDY2ZWY2LXNiLmJhYXMubmludGVuZG8uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtwZRFUFinbzSF9aCaLqSdDSfS4Lfz6bSB+4ymwqkgJPLMLtX0meEVOSsx2qP/OQFkb6/RAZk3GK2CSM46V8A+bZqYn1jHyRyQeQmAfrzvXxAiXBdSq4Eso1C/roFQDjR3p+RnLWDLdMPUHkmAQSPEpVAUbcCvUD8zZwQ+go8GAzG0iQNNvZxma1JQoDsHPgY5LiBVBlqY43tp/jeW/sb3SxZ30zzIckKtQfYDfph/vns+q1/raNRPgS0TQw4E470n27UgLE30yoVn/DlYwmtZneCOWIByVPrMBMIkrv/Gh9VW3GszbqZz2u3K9Aj7rtnCtmUrI9dQbeTnruWOn/FjwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBsXUY5j/m4pAB0R3zDEHHBXZUCkdN53zCr5M7/Kn4ak0vKb9Hk1gNJqdS+dNyW1C7aNGFPmmcMhOos3tv0KsjNoftA4A27uIA7U6QYtJSUVbtVJgYCu8XUa2VDongGNxB72UvFiD++qY3Xe95b0kzsoSOqGaguicAeRSG/HARDaNJT5I9MhCYdCmjU/z83dGI0t8v33Zua92YVmzL6E06lxQ7KrVa9MlKgVJEiAnju3GESVFmrhEoVt516EVxvTVmFlrsvN6rh+c/jC3mBDkE7E23ukg6FpM8oCVRU/5o8/lnKVcRhYPnBgHwg/wTL/Is5rteMtLihV/mvgt3InWkc'],
    u'usage': u'developer', 
    u'kid': u'fe9dbbff-470b-46c8-8baf-1b7698dee5be'
}

Traceback (most recent call last):
  File "./saqib_test.py", line 46, in <module>
    jwt.decode(signature, curr_jwk['x5c'][0], algorithms=['RS256'])
  File "/Users/saqib.ali/saqib-env-987/lib/python2.7/site-packages/jwt/api_jwt.py", line 84, in decode
    payload, _, _, _ = self._load(jwt)
  File "/Users/saqib.ali/saqib-env-987/lib/python2.7/site-packages/jwt/api_jws.py", line 183, in _load
    raise DecodeError('Not enough segments')
jwt.exceptions.DecodeError: Not enough segments
Saqib Ali
  • 11,931
  • 41
  • 133
  • 272

2 Answers2

1

First, you should directly decode base64 encoded message. That is a whole jwt message, not just the signature. Which should be:

jwt.decode(my_jwt, algorithms='RS256')

Second, it won't work because by default there isn't RS256 support. You can refer to this issue: https://github.com/jpadilla/pyjwt/issues/181

Finally, I don't think you can decode this RS256 encoded message without public key. Do you have that key?

Sraw
  • 18,892
  • 11
  • 54
  • 87
  • I'm not sure if I have the public key. I think I do. But I'm not 100% sure. Is `curr_jwk['x5c'][0]` the public key? – Saqib Ali Dec 19 '18 at 23:33
  • Well, honestly I don't know. You can have a try. If you succeed, then it is. – Sraw Dec 19 '18 at 23:36
  • My understanding is that you can decode RS256 token (which is just base64 encoded - use argument: verify=False) - but you cannot **verify** it without the public key. – pythonjsgeo Aug 05 '19 at 23:35
1

Your making your decoding process very difficult.

This is a one liner to decode JWT tokens:

JWT_SECRET = 'secretpassphrase'
JWT_ALGORITHM = 'HS256'
decoded = json.loads(json.dumps(jwt.decode(token, JWT_SECRET, JWT_ALGORITHM)))

What this line does is decodes the token using the secret password that was used to generate it then, "json.dumps" loads it into byte format, next "json.loads" allows you to parse it with the code below.

After this your able to get the individual attributes of the token like this:

user_id = decoded['user_id']
expiry_time = decoded['exp']

NOTE: This should be put into a Try: Catch statement because if the token is expired or doesn't use the same signature then python will throw an error. Either Invalid Signature or Token has Expired.

def verify_token(token):
    try:
        decoded = json.loads(json.dumps(jwt.decode(token, JWT_SECRET, JWT_ALGORITHM)))
        return {
           'name': decoded['user_id'],
           'exp': decoded['exp']
           }
    except:
        print("Generic Error")

Hope this helps

D3vRandom
  • 155
  • 1
  • 1
  • 12
  • 1
    It really helped me thanks so much. I have been looking at this jwt token issue for a while in my Lambda function but couldn't really figure out how to do it. @D3vRandom your comment truly came at the right time for me. – flyiinhigh Jul 26 '20 at 02:57