mpdavis / python-jose

A JOSE implementation in Python
MIT License
1.54k stars 237 forks source link

pyjwt verifies token while python-jose fails #90

Closed jonathan-kosgei closed 6 years ago

jonathan-kosgei commented 6 years ago

This pyjwt example works;

import jwt

public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumZZl1U3GFFZyVTRmHLg\nb1II9+fOIqg9CT4gGDyfLglsPMBV3m6G88KhgiStpnY/nmR/yx0PewIBYPJNEC6x\nxdKxDbKkIA7oZz+P+I1qJwYQsyhIfmVd9IwGIebYu1ZNrlJmseu4axi+Q3NbjRs4\nsvXDt/WF4bkmGIvdlt35xta7+Djo+WiGWfFZBaurnDZqtIZ4xl/CJW0rByX1hBHS\nUn/sS4JL8YUnPC8vLDUXlG5sLH/7BTI1VMtpWWqROnY9B/J8fR6oDdaSWP/BaYQQ\nr8g6ye3a95zpaTweTNnom2VMgj9g23qPYrKD9zXL4oXTjjTb0MbUHRLP8FcYI7E5\nSwIDAQAB\n-----END PUBLIC KEY-----\n"
token = "eyJraWQiOiJ3bXF3Q2ttbVFubll1RXJEVGU2MDVOWUdMR0VTSW5iWUVmd3ZBeXJHc053PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJiMTMzZWQ0ZC02ZmUwLTQ0YjgtYjZiOS00YWY4NmJkMDYzMmQiLCJldmVudF9pZCI6IjcwNGEzODc3LTU0NGMtMTFlOC04YzRjLTZkNDNmZDlhNDg2YiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1MjU5NTQ3OTEsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xX21yazRSeWpZeiIsImV4cCI6MTUyNTk1ODM5MSwiaWF0IjoxNTI1OTU0NzkxLCJqdGkiOiJjN2EzZjE1MC1jM2I3LTQzOWQtOWRiNS1jNzMxNDgzNzc3MjIiLCJjbGllbnRfaWQiOiI2dXBlNm5kdW5ka3Y3ZjVpOWpsbzZhOHAydCIsInVzZXJuYW1lIjoiam9uYXRoYW5rb3NnZWkxMDAwQGdtYWlsLmNvbSJ9.extzT3KMtocdKmuNgpUpOAUe2WgOmEV2TbO4yWS8nnNzugIlYx93od38WKxLR66x1qTJVv-YQ-Yuk0pt2Nh-bWbYbOmYpURNBAVeFoLILxOMcGtboRI8ecBN57KZt6EQZl9_4gJmSqYDC3yXPBWyZ1MpDItaZCEbOEHIg8CEoCgTyeo5H_-AH7jBBSOLJF1rzdqntVkaVeCO91Zc-L13ZNEpaxtNH95IKhn7XWD0vWvmnjYvHH4xe7iuOE-9zg9QTtb4tJvSdfkRYakfuJ-cqHaHOYFUu50n-rVs8H6Rr_fi_vohxC7ksdglhytg7K0COtvLSiJAFoZpuUW8QPF2lA"

decoded_payload = jwt.decode(token, key=public_key, algorithms=['RS256'])

While the same example with python-jose fails

from jose import jwk
from jose.utils import base64url_decode
token = "eyJraWQiOiJ3bXF3Q2ttbVFubll1RXJEVGU2MDVOWUdMR0VTSW5iWUVmd3ZBeXJHc053PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJiMTMzZWQ0ZC02ZmUwLTQ0YjgtYjZiOS00YWY4NmJkMDYzMmQiLCJldmVudF9pZCI6IjcwNGEzODc3LTU0NGMtMTFlOC04YzRjLTZkNDNmZDlhNDg2YiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1MjU5NTQ3OTEsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xX21yazRSeWpZeiIsImV4cCI6MTUyNTk1ODM5MSwiaWF0IjoxNTI1OTU0NzkxLCJqdGkiOiJjN2EzZjE1MC1jM2I3LTQzOWQtOWRiNS1jNzMxNDgzNzc3MjIiLCJjbGllbnRfaWQiOiI2dXBlNm5kdW5ka3Y3ZjVpOWpsbzZhOHAydCIsInVzZXJuYW1lIjoiam9uYXRoYW5rb3NnZWkxMDAwQGdtYWlsLmNvbSJ9.extzT3KMtocdKmuNgpUpOAUe2WgOmEV2TbO4yWS8nnNzugIlYx93od38WKxLR66x1qTJVv-YQ-Yuk0pt2Nh-bWbYbOmYpURNBAVeFoLILxOMcGtboRI8ecBN57KZt6EQZl9_4gJmSqYDC3yXPBWyZ1MpDItaZCEbOEHIg8CEoCgTyeo5H_-AH7jBBSOLJF1rzdqntVkaVeCO91Zc-L13ZNEpaxtNH95IKhn7XWD0vWvmnjYvHH4xe7iuOE-9zg9QTtb4tJvSdfkRYakfuJ-cqHaHOYFUu50n-rVs8H6Rr_fi_vohxC7ksdglhytg7K0COtvLSiJAFoZpuUW8QPF2lA"
rsa_key = {"alg":"RS256","e":"AQAB","kid":"wmqwCkmmQnnYuErDTe605NYGLGESInbYEfwvAyrGsNw=","kty":"RSA","n":"umZZl1U3GFFZyVTRmHLgb1II9-fOIqg9CT4gGDyfLglsPMBV3m6G88KhgiStpnY_nmR_yx0PewIBYPJNEC6xxdKxDbKkIA7oZz-P-I1qJwYQsyhIfmVd9IwGIebYu1ZNrlJmseu4axi-Q3NbjRs4svXDt_WF4bkmGIvdlt35xta7-Djo-WiGWfFZBaurnDZqtIZ4xl_CJW0rByX1hBHSUn_sS4JL8YUnPC8vLDUXlG5sLH_7BTI1VMtpWWqROnY9B_J8fR6oDdaSWP_BaYQQr8g6ye3a95zpaTweTNnom2VMgj9g23qPYrKD9zXL4oXTjjTb0MbUHRLP8FcYI7E5Sw","use":"sig"}
key = jwk.construct(rsa_key)
message, encoded_sig = token.rsplit('.', 1)
decoded_sig = base64url_decode(encoded_sig.encode('utf-8'))
key.verify(message, decoded_sig)

That is key.verify returns False

zejn commented 6 years ago

Hi, You will need to specify a bit more about your setup: which Python version, which python-jose version, which python-jose backend you are using (if any) and what is the error you get (if any).

mpdavis commented 6 years ago

As @zejn mentioned, some more information would be helpful. That token verifies successfully for me with every available RSA backend with python-jose 3.0.0

The output from a pip freeze, as well as the python version you are using would be at the top of the list of things I would want to see.

jonathan-kosgei commented 6 years ago

@mpdavis @zejn

I'm using python-jose 3.0.0 and Python 3.6.3.

@mpdavis just to confirm the above code works for you and key.verify gives you True?

zejn commented 6 years ago

python-jose will use other libraries if installed, so can you please add output from pip freeze or pip list?

jonathan-kosgei commented 6 years ago

Output of pip list;

alabaster (0.7.10)
ansible (2.4.1.0)
apilogs (1.2)
apturl (0.5.2)
arrow (0.12.1)
asn1crypto (0.22.0)
awscli (1.14.2)
awslogs (0.10.0)
Babel (2.5.3)
bcrypt (3.1.4)
beautifulsoup4 (4.6.0)
blinker (1.3)
boto3 (1.5.35)
botocore (1.8.49)
Brlapi (0.6.5)
cachetools (2.0.1)
certifi (2017.4.17)
cffi (1.11.2)
chardet (3.0.4)
click (6.7)
colorama (0.3.7)
command-not-found (0.3)
cryptography (1.9)
cupshelpers (1.0)
defer (1.0.6)
distro-info (0.17)
dnspython (1.15.0)
docutils (0.14)
ecdsa (0.13)
emoji-flags (0.1.0)
emojiflags (0.1.0)
feedparser (5.1.3)
Flask (0.12.2)
future (0.16.0)
google-auth (1.2.1)
html5lib (0.999999999)
httplib2 (0.9.2)
idna (2.5)
imagesize (1.0.0)
IP2Location (8.0.0)
ipaddress (1.0.18)
ipwhois (1.0.0)
itsdangerous (0.24)
japronto (0.1.1)
Jinja2 (2.9.6)
jmespath (0.9.3)
keyring (10.4.0)
keyrings.alt (2.2)
kubernetes (4.0.0)
language-selector (0.1)
launchpadlib (1.10.5)
lazr.restfulclient (0.13.5)
lazr.uri (1.0.3)
livereload (2.5.1)
louis (3.0.0)
lxml (4.0.0)
Mako (1.0.7)
Markdown (2.6.9)
MarkupSafe (1.0)
mkdocs (0.16.3)
mkdocs-material (1.12.2)
nltk (3.2.5)
oauth (1.0.1)
oauthlib (2.0.1)
ObjDict (0.4.4)
olefile (0.44)
packaging (17.1)
paramiko (2.3.1)
pexpect (4.2.1)
Pillow (4.1.1)
pip (9.0.1)
pyasn1 (0.4.2)
pyasn1-modules (0.2.1)
pycountry (18.2.23)
pycparser (2.18)
pycrypto (2.6.1)
pycups (1.9.73)
Pygments (2.2.0)
pygobject (3.24.1)
PyJWT (1.4.2)
pymdown-extensions (4.1)
pymongo (3.4.0)
Pympler (0.5)
PyNaCl (1.1.2)
pyparsing (2.2.0)
python-apt (1.4.0b3)
python-dateutil (2.6.1)
python-debian (0.1.30)
python-jose (3.0.0)
python-lambda-local (0.1.5)
python-whois (0.6.8)
pytz (2017.3)
pyvips (2.1.3)
pyxdg (0.25)
PyYAML (3.12)
redis (2.10.6)
reportlab (3.4.0)
requests (2.18.1)
requests-oauthlib (0.8.0)
rq (0.10.0)
rq-dashboard (0.3.10)
rsa (3.4.2)
s3transfer (0.1.13)
SecretStorage (2.3.1)
setuptools (36.2.7)
simplejson (3.11.1)
six (1.11.0)
snowballstemmer (1.2.1)
Sphinx (1.7.4)
sphinx-rtd-theme (0.3.0)
sphinxcontrib-websupport (1.0.1)
stripe (1.75.0)
system-service (0.3)
systemd-python (234)
termcolor (1.1.0)
tornado (4.5.2)
ubuntu-drivers-common (0.0.0)
ufw (0.35)
unattended-upgrades (0.1)
unity-scope-calculator (0.1)
unity-scope-chromiumbookmarks (0.1)
unity-scope-colourlovers (0.1)
unity-scope-devhelp (0.1)
unity-scope-firefoxbookmarks (0.1)
unity-scope-manpages (0.1)
unity-scope-openclipart (0.1)
unity-scope-texdoc (0.1)
unity-scope-tomboy (0.1)
unity-scope-virtualbox (0.1)
unity-scope-yelp (0.1)
unity-scope-zotero (0.1)
urllib3 (1.21.1)
usb-creator (0.3.3)
uvloop (0.8.1)
wadllib (1.3.2)
webencodings (0.5)
websocket-client (0.40.0)
Werkzeug (0.14.1)
xkit (0.0.0)
youtube-dl (2017.9.24)
zope.interface (4.3.2)
mpdavis commented 6 years ago

Correct.

~ ❯❯❯ python --version
Python 2.7.11
~ ❯❯❯ pip freeze | grep python-jose
python-jose==3.0.0
~ ❯❯❯ python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from jose import jwk
>>> from jose.utils import base64url_decode
>>> token = "eyJraWQiOiJ3bXF3Q2ttbVFubll1RXJEVGU2MDVOWUdMR0VTSW5iWUVmd3ZBeXJHc053PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJiMTMzZWQ0ZC02ZmUwLTQ0YjgtYjZiOS00YWY4NmJkMDYzMmQiLCJldmVudF9pZCI6IjcwNGEzODc3LTU0NGMtMTFlOC04YzRjLTZkNDNmZDlhNDg2YiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1MjU5NTQ3OTEsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xX21yazRSeWpZeiIsImV4cCI6MTUyNTk1ODM5MSwiaWF0IjoxNTI1OTU0NzkxLCJqdGkiOiJjN2EzZjE1MC1jM2I3LTQzOWQtOWRiNS1jNzMxNDgzNzc3MjIiLCJjbGllbnRfaWQiOiI2dXBlNm5kdW5ka3Y3ZjVpOWpsbzZhOHAydCIsInVzZXJuYW1lIjoiam9uYXRoYW5rb3NnZWkxMDAwQGdtYWlsLmNvbSJ9.extzT3KMtocdKmuNgpUpOAUe2WgOmEV2TbO4yWS8nnNzugIlYx93od38WKxLR66x1qTJVv-YQ-Yuk0pt2Nh-bWbYbOmYpURNBAVeFoLILxOMcGtboRI8ecBN57KZt6EQZl9_4gJmSqYDC3yXPBWyZ1MpDItaZCEbOEHIg8CEoCgTyeo5H_-AH7jBBSOLJF1rzdqntVkaVeCO91Zc-L13ZNEpaxtNH95IKhn7XWD0vWvmnjYvHH4xe7iuOE-9zg9QTtb4tJvSdfkRYakfuJ-cqHaHOYFUu50n-rVs8H6Rr_fi_vohxC7ksdglhytg7K0COtvLSiJAFoZpuUW8QPF2lA"
>>> rsa_key = {"alg":"RS256","e":"AQAB","kid":"wmqwCkmmQnnYuErDTe605NYGLGESInbYEfwvAyrGsNw=","kty":"RSA","n":"umZZl1U3GFFZyVTRmHLgb1II9-fOIqg9CT4gGDyfLglsPMBV3m6G88KhgiStpnY_nmR_yx0PewIBYPJNEC6xxdKxDbKkIA7oZz-P-I1qJwYQsyhIfmVd9IwGIebYu1ZNrlJmseu4axi-Q3NbjRs4svXDt_WF4bkmGIvdlt35xta7-Djo-WiGWfFZBaurnDZqtIZ4xl_CJW0rByX1hBHSUn_sS4JL8YUnPC8vLDUXlG5sLH_7BTI1VMtpWWqROnY9B_J8fR6oDdaSWP_BaYQQr8g6ye3a95zpaTweTNnom2VMgj9g23qPYrKD9zXL4oXTjjTb0MbUHRLP8FcYI7E5Sw","use":"sig"}
>>> key = jwk.construct(rsa_key)
>>> message, encoded_sig = token.rsplit('.', 1)
>>> decoded_sig = base64url_decode(encoded_sig.encode('utf-8'))
>>> key.verify(message, decoded_sig)
True
>>>
zejn commented 6 years ago

I see. On Python 3.5.3 key.verify(message, decoded_sig) returns False, while key.verify(message.encode('utf-8'), decoded_sig) returns True. You're probably using pycrypto backend, as with cryptography I get a TypeError.

jonathan-kosgei commented 6 years ago

Thanks, that works.

I got here from https://github.com/awslabs/aws-support-tools/blob/master/Cognito/decode-verify-jwt/decode-verify-jwt.py and the AWS Documentation at https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/

Their sample code doesn't work without modification for Python 3.

zejn commented 6 years ago

@jonathan-kosgei If you can, migrate away from pycrypto. For python-jose it's enough to uninstall pycrypto and it will start using cryptography.

mpdavis commented 6 years ago

@jonathan-kosgei A potential quick fix would be to pip uninstall pycrypto if you don't absolutely need pycrypto.

That will fall back to using cryptography as an RSA backend, which is the suggested backend anyway.

I see a couple of issues to look into. First, is the TypeError coming from pycrpyto.

Second, we should probably alter the order in which we look for backends. Currently, it uses pycrypto first if it is installed, but pycrypto should only be used as the last resort. We should change the order we try to import those.

jonathan-kosgei commented 6 years ago

It worked even before uninstalling pycrypto so long as I used encode('utf-8'), I've uninstalled pycrypto but run into TypeError: must be str, not bytes if I don't encode the values as utf-8 first.