如何使用带有公共 PEM 证书的 python PyJWT 验证 JWT?

Posted

技术标签:

【中文标题】如何使用带有公共 PEM 证书的 python PyJWT 验证 JWT?【英文标题】:How to verify a JWT using python PyJWT with a public PEM cert? 【发布时间】:2015-06-16 13:18:43 【问题描述】:

我最近从 PyJWT 0.4.1 升级到 1.0.1,但我不知道如何验证使用公钥签名的 JWT。

我的代码如下所示:

import jwt
cert_string = "-----BEGIN CERTIFICATE-----\nMIICITCCAYqgAwIBAgIIBEsUSxL..."
token_string = "eyJhbGciOiJSUzI1NiIsImtpZCI6I..."
jwt.decode(token_string, cert_string, algorithms=['RS256'])

我得到的错误是:

File "<stdin>", line 1, in <module>
File "~/.virtualenvs/project/lib/python2.7/site-packages/jwt/api.py", line 117, in decode                            
key, algorithms, **kwargs)                                                                                         
File "~/.virtualenvs/project/lib/python2.7/site-packages/jwt/api.py", line 176, in _verify_signature                 
key = alg_obj.prepare_key(key)                                                                                     
File "~/.virtualenvs/project/lib/python2.7/site-packages/jwt/algorithms.py", line 165, in prepare_key                
key = load_pem_public_key(key, backend=default_backend())                                                          
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/primitives/serialization.py", line 24, in load_pem_public_key
return backend.load_pem_public_key(data)                                                                           
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/backends/multibackend.py", line 285, in load_pem_public_key
return b.load_pem_public_key(data)
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 716, in load_pem_public_key
self._handle_key_loading_error()
File "~/.virtualenvs/project/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 912, in _handle_key_loading_error
raise ValueError("Could not unserialize key data.")                                                                
ValueError: Could not unserialize key data.

我相信我的 cert_string 和 token 是好的。以下代码运行正常:

from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend
load_pem_x509_certificate(cert_string, default_backend())

我曾经使用 0.4.1 的代码如下所示:

cert_string = "".join(cert_string.strip().split("\n")[1:-1])
der = a2b_base64(cert_string)
cert = DerSequence()
cert.decode(der)
tbsCertificate = DerSequence()
tbsCertificate.decode(cert[0])
subjectPublicKeyInfo = tbsCertificate[6]
pub_key = RSA.importKey(subjectPublicKeyInfo)
jwt.decode(token_string, pub_key)

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

您需要将公钥而不是完整证书传递给解码方法。因此,从证书中提取密钥以便按如下方式使用它:

from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

cert_str = "-----BEGIN CERTIFICATE-----MIIDETCCAfm..."
cert_obj = load_pem_x509_certificate(cert_str, default_backend())
public_key = cert_obj.public_key()

然后:

token_string = "eyJhbGciOiJSUzI1NiIsImtpZCI6I..."
jwt.decode(token_string, public_key, algorithms=['RS256'])

【讨论】:

收到错误TypeError: initializer for ctype 'char[]' must be a bytes or list or tuple, not str 这对我有用,但我的证书需要证书中的 \n。例如cert_str = "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIUAu/wmcX4dQwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2... 在 Python 3 中,load_pem_x509_certificate 期望 cert_str 是字节而不是字符串。如果你传递一个字符串,你会得到TypeError: from_buffer() cannot return the address of a unicode object 在 Python3 中,解决方法是给您的密钥添加后缀编码,即 cert_obj.encode('utf-8')【参考方案2】:

如果密钥文件是使用ssh-keygen -t rsa(RFC4716)生成的,则可以直接使用该文件。

编码:

import jwt

pemfile = open("id_rsa", 'r')
keystring = pemfile.read()
pemfile.close()
token = jwt.encode(payload, keystring, algorithm='RS256')

解码:

import jwt

pemfile = open("id_rsa.pub", 'r')
keystring = pemfile.read()
pemfile.close()
payload = jwt.decode(toekn, keystring,  verify=True)

不要忘记捕捉jwt.ExpiredSignatureError 等错误

【讨论】:

【参考方案3】:

我正在使用 python 3.6

import jwt
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
key = b"""-----BEGIN PUBLIC KEY-----
MIIBI....<<Your public key basically>>
-----END PUBLIC KEY-----"""
public_key = serialization.load_pem_public_key(key, backend=default_backend())
print(jwt.decode(token, public_key))

这对我来说就像一个魅力。

【讨论】:

不幸的是,使用 Python 3.6(不要问)我得到 Message=from_buffer() cannot return the address of a unicode object

以上是关于如何使用带有公共 PEM 证书的 python PyJWT 验证 JWT?的主要内容,如果未能解决你的问题,请参考以下文章

百度推送证书在终端如何合成?一行代码将.p12证书合成.pem证书

如何从一个 .crt(通配符)文件生成多个 .pem 文件?

如何通过 PHP 发送带有 pem 证书的 curl 请求?

Python p12 到 pem

DER,CRT,CER,PEM证书以及如何转换它们

openssl .p12 证书只有一个串联的 .pem 证书信息