概要
jwk は鍵を公開鍵管理する API で JSON 形式で取得できます
今回は jwk で取得した鍵情報を使用してそこから jwt を復号化する方法を紹介します
なお暗号化方式は ES512 で楕円曲線演算方法は P-521 の場合になります
鍵などの情報は適当に記載しています
環境
- macOS 11.6.4
- Python 3.10.2
- pyjwt 2.3.0
サンプルコード
import jwt
import json
from jwt.algorithms import ECAlgorithm
from cryptography.hazmat.primitives import serialization
jwks = '{"keys":[{"alg":"ES512","crv":"P-521","kid":"key_id1","kty":"EC","use":"sig","x":"66_bytes_characters","y":"65_or_66_bytes_characters"}]}'
jwks_dict = json.loads(jwks)
jwt_token = "encripted_json_web_token"
# トークンから kid や alg などのヘッダ情報を取得
header = jwt.get_unverified_header(jwt_token)
# print(header)
# 公開鍵の一覧から kid に一致する jwk を取得
jwk_json = [jwk for jwk in jwks_dict['keys'] if jwk['kid'] == header['kid']]
# 公開鍵情報から暗号化するための pem キーを生成
public_key = ECAlgorithm.from_jwk(jwk_json[0])
pem = public_key.public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)
# pem キーを使って jwt トークンを復号化する
decoded_jwt = jwt.decode(jwt_token, pem, algorithms=jwk_json[0]['alg'])
print(decoded_jwt)
軽く説明
まず jwk が公開してる鍵情報の JSON を jwks に設定しています
鍵が複数ある場合もあるので今回は複数の鍵を想定しています
まずトークン情報から jwt.get_unverified_header
を使ってヘッダ情報を取得します
ここに暗号化した鍵の kid やアルゴリズムが記載されています
次に kid を元に公開鍵の一覧から復号化するための jwk を取得します
ヘッダに含まれる kid を使って合致する公開鍵を設定します
取得した jwk から PEM キーを生成します
生成するには ECAlgorithm.from_jwk を使います
あとは jwt.decode を使ってトークンをデコードします
pyjwt にパッチを当てる必要がある
実は pyjwt をそのまま使った場合だと動作しない場合があります
jwk で公開している楕円曲線演算方法が P-521 の場合 Y の鍵が 66 bytes である必要があります
しかし実際は 65 or 64 bytes (64bytes である可能性は 0.2%) になる可能性があり pyjwt では 65 or 64 bytes のときを考慮しておりません
なので以下のように条件を変更するパッチを当てる必要があります
- vim /path/to/venv/site-packages/jwt/algorithms.py
elif curve == "P-521":
if len(x) == 66 and (len(y) == 66 or len(y) == 65):
curve_obj = ec.SECP521R1()
else:
raise InvalidKeyError("Coords should be 66 bytes for curve P-521")
最後に
jwk は基本的に https 経由で取得することになると思います
jwt も REST などの API 経由のレスポンスで取得することになると思います
pyjwt は修正が必要なので別の jwt ライブラリを使えばパッチを必要はないかもしれません (jose)
0 件のコメント:
コメントを投稿