首先,Demonware/JOSE 项目最初是为 Python 2 编写的,您大概使用的是Python 3 分支。实现中存在未解决的问题,对我来说,包作者实际上并没有很好地理解这个问题。JWT 令牌在其紧凑的序列化中只是一系列与字符连接的 URL 安全 Base64 字符串.
。
每当您加密或签署新令牌时,都必须将字节解码为字符串(只需将字节值解码为 ASCII)。验证或解密时,您需要再次将字符串编码为字节。
例如,要对jwt
JSON 对象中的值进行编码,您需要解码:
data = {'jwt': jwt.decode('ascii')}
Javascript Web Token (JWT) 的全部意义在于作为文本与另一方进行交换。该库通过声称该方法返回一个字符串来复合。
您可以使用默认编解码器缩短它utf-8
,因为 ASCII 是 UTF-8 的子集:
data = {'jwt': jwt.decode()}
在相反的方向,您必须再次将紧凑字符串编码为字节:
data = json.loads(json_document)
jose.decrypt(jose.deserialize_compact(data['jwt'].encode()), priv_jwk)
但是您基本上使用的是过时的软件;Demonware/jose 项目已经 3 年没有更新了。它还依赖于过时的、未维护的 pycrypto 包。你不想用。
相反,请查看Authlib或JWCrypto,这两个模块正在积极维护,并使用该cryptography
项目来处理棘手的密码学原语(还有pyjwt
和python-jose
,但这些项目不(尚)支持 JWE 加密,仅支持 JWS 签名令牌) .
其中,Authlib 提供了迄今为止最干净、最清晰的 API。例如,使用 Demonware/JOSE 选择的默认加密和签名算法,生成公钥/私钥对并使用 Authlib 创建加密令牌,您可以:
from authlib.jose import jwt
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
# generate a private key with corresponding public key, then
# export the keys as a PEM serializations.
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
private_key_pem = private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption()
)
public_key = private_key.public_key()
public_key_pem = public_key.public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo
)
# create an encrypted token (JWT using JWE)
claims = {'name': 'Jack'}
header = {'enc': 'A128CBC-HS256', 'alg': 'RSA-OAEP'}
token = jwt.encode(header, claims, public_key_pem).decode()
# decrypt the token again
claims_from_token = jwt.decode(token.encode(), private_key_pem)
请注意,Authlib 也在bytes
此处返回一个值,因此如果您想进一步将其嵌入 JSON 并将令牌数据从 JSON 有效负载传递回jwt.decode()
方法,您也必须对其进行解码和编码。
使用与 JWCrypto 相同的公钥和私钥序列化:
import json
from jwcrypto import jwt, jwk
# Create a JWK key object from the private key
jwk_key = jwk.JWK.from_pem(private_key_pem)
# create a JWT() instance to handle ecryption and serialization
header = {'enc': 'A128CBC-HS256', 'alg': 'RSA-OAEP'}
jwt_token = jwt.JWT(claims, header)
jwt_token.make_encrypted_token(jwk_key)
token = jwt_token.serialize()
# deserialize again, using a new JWT instance but with different arguments;
# Yes, this is a confusing API.
# it's always a good idea to limit what algorithms you'll accept
# note that this is a list of both *signing* and *encryption* algorithms, not
# the possible values of the "alg" key in a JOSE header.
jwt_token = jwt.JWT(key=jwk_key, jwt=token, algs=['A128CBC-HS256', 'RSA-OAEP'])
claims_from_token = json.loads(jwt_token.claims)
请注意,此库在序列化时确实会返回一个字符串,但您必须手动对声明进行 JSON 解码。API 还将序列化和反序列化混合到一个类中,从而创建了参数、方法和属性的非常混乱的组合。