2022年3月3日木曜日

pyjwt で jwk を使って認証情報を取得しそこから jwt を復号化する方法

pyjwt で jwk を使って認証情報を取得しそこから jwt を復号化する方法

概要

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 件のコメント:

コメントを投稿