本文へスキップ
ecdsa.com
Python

JWCrypto

Red Hat の JWCrypto は、FreeIPA や多くのエンタープライズ向け Python サービスで使われている JOSE 実装です。ML-DSA を含めて JWS、JWE、JWK を網羅し、時刻のずれの許容はデフォルトで 60 秒、alg: none は明示的に求めた場合にだけ受け入れます。
ライセンス
LGPL-3.0-or-later
確認したバージョン
1.6.1 ·
ソースコード
github.com/latchset/jwcrypto
ドキュメント
jwcrypto.readthedocs.io/en/latest/

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSAEd25519Ed448ML-DSA-44ML-DSA-65ML-DSA-87

JWCrypto は、pyca/cryptography の上に JOSE ファミリー(JWK と JWK セット、JWS、JWE、JWT)を実装しています。すべてが鍵オブジェクト、つまり PEM、JSON、generate() から作成した jwk.JWK を基本としているため、どの呼び出しでも鍵の種類が明示されます。ライセンスは LGPL-3.0-or-later で、改変したコピーを再配布する場合にはこの点が重要になります。

対応するアルゴリズムは Python の JOSE ライブラリの中で最も幅広く、HS、RS、PS、ES の各ファミリー、ES256K、EdDSA、完全指定の Ed25519 と Ed448、さらに 1.6.1 では、ポスト量子 JOSE のドラフトで定義された AKP 鍵による ML-DSA-44、ML-DSA-65、ML-DSA-87 にも対応します。チェックではいずれも署名と検証ができ、すべてがデフォルトの許可リストに含まれています。注意すべきなのはこのデフォルトのリストです。algs を渡さない限り、鍵が合えばどのアルゴリズムも受け入れられるため、サンプルのように、発行者が実際に使うものに絞り込んでください。

none を列挙しない限り、署名のないトークンは Algorithm not allowed で拒否されます。また、EC 公開鍵の JWK を指定した HS256 トークンは失敗します。その種類の鍵では HMAC を実行できないためです。ES256 は P-384 の鍵を Curve requested is "P-256", but key curve is "P-384" で拒否します。ES256 署名は 64 バイトの生の r‖s 形式で、DER の署名は Verification failed for all signatures で失敗します。エンコーディングは JWT デバッガーで確認してください。

2 つのデフォルトはほかのライブラリと異なります。有効期限のチェックでは 60 秒の時刻のずれが許容されます。57 秒前に期限切れになったトークンはまだ検証を通り、63 秒前に期限切れになったトークンは通りませんでした。ずれをまったく許容しない PyJWT や jose よりも寛容です。また、exp は任意で、check_claims で指定しない限り、このクレームのないトークンも検証を通りました。

鍵セットは JWKS ビルダーで作成し、暗号化されたトークンは JWE インスペクターで解析してください。ML-DSA の鍵と署名については、ポスト量子署名ラボでそれぞれのサイズを確認できます。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/jwcrypto/run.sh がコンテナイメージ python:3.14.8-slim-trixie 内で JWCrypto 1.6.1 を cryptography 50.0.2 で実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSAEd25519Ed448ML-DSA-44ML-DSA-65ML-DSA-87

    256/384/512 ビットの HS、RS、PS、ES、ES256K、EdDSA、Ed25519、Ed448、ML-DSA-44/65/87(AKP 鍵)で署名と検証ができました。いずれもデフォルトの許可リストに含まれています。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, EdDSA, Ed25519, Ed448, ML-DSA-44, ML-DSA-65, ML-DSA-87
    evidence jws-algorithms: ML-DSA-44: JWK kty AKP
    evidence jws-algorithms: ML-DSA-65: JWK kty AKP
    evidence jws-algorithms: ML-DSA-87: JWK kty AKP
    evidence jws-algorithms: default allowed list: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, EdDSA, ES256K, Ed25519, Ed448, ML-DSA-44, ML-DSA-65, ML-DSA-87
  • ES256 署名のエンコーディング

    raw

    ES256 署名は 64 バイトの生の r‖s です。

    チェックによる記録
    fact jws-ecdsa-encoding = raw
  • JWS 内の DER 署名

    rejected

    DER でエンコードされた ES256 署名は、InvalidJWSSignature: Verification failed for all signatures で失敗します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
  • アルゴリズムの許可リスト

    from-key

    algs を指定しない場合はデフォルトのリストが適用され、鍵の種類で決まります。ES256 トークンは EC 鍵で検証でき、同じ鍵に対する RS256 トークンは失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: no algs argument: verified; RS256 token with the EC key: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
  • 署名なしトークン(alg none)

    opt-in

    署名のないトークンは、デフォルトでは Algorithm not allowed で失敗し、algs=["none"] を指定した場合にだけ検証を通ります。

    チェックによる記録
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with the EC key: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSOperation('Algorithm not allowed')]"]
    evidence jwt-alg-none: algs ['none'] with an empty oct key: verified
  • HMAC シークレットとして使われる公開鍵

    rejected

    EC 公開鍵の JWK で検証した HS256 トークンは失敗しました。検証を通ったのは、PEM テキストを含む oct JWK を意図的に作成した場合だけです。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: HS256 token checked with the EC public JWK: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
    evidence jwt-hmac-public-key: only an explicit oct JWK made from the PEM text verifies it: True
  • alg と曲線の照合

    enforced

    P-384 の鍵で ES256 の署名を行うと、Curve requested is "P-256", but key curve is "P-384" が発生します。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidJWKValue: Curve requested is "P-256", but key curve is "P-384"
  • デフォルトのクロックスキュー

    60s

    有効期限のチェックでは、デフォルトで 60 秒の時刻のずれが許容されます。57 秒前に期限切れになったトークンは検証を通り、63 秒前に期限切れになったトークンは通りませんでした。

    チェックによる記録
    fact jwt-clock-skew = 60s
  • 有効期限クレームの要否

    no

    exp のないトークンも検証を通りました。必須にするには check_claims に exp を列挙してください。

    チェックによる記録
    fact jwt-exp-required = no
    evidence jwt-exp-required: verified

使い方

鍵 ID を付けて ES256 トークンに署名し、アルゴリズムの一覧を絞り込み、iss と exp を必須にして検証します。
import json
import time

from jwcrypto import jwk, jwt

key = jwk.JWK.generate(kty="EC", crv="P-256", kid="2026-10")
public = jwk.JWK.from_json(key.export_public())

token = jwt.JWT(header={"alg": "ES256", "kid": "2026-10"}, claims={"sub": "user-42", "iss": "https://auth.example.com", "exp": int(time.time()) + 600})
token.make_signed_token(key)

# algs narrows the default list; check_claims makes iss and exp mandatory.
checked = jwt.JWT(
    jwt=token.serialize(),
    key=public,
    algs=["ES256"],
    check_claims={"iss": "https://auth.example.com", "exp": None},
)
print("sub:", json.loads(checked.claims)["sub"])

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ python:3.14.8-slim-trixie 内でそのまま実行し、出力が一致しなければ実行は失敗します。

セットアップ
pip install --quiet --root-user-action=ignore jwcrypto==1.6.1 cryptography==50.0.2
コマンド
python usage.py
一致すべき出力(正規表現)
sub: user-42

ライブラリ