本文へスキップ
ecdsa.com
Java · Kotlin

java-jwt (Auth0)

Auth0 の java-jwt は、コンパクトな JWT ライブラリです。各検証器は 1 つの Algorithm オブジェクトに結び付けられ、JWT.decode() は何もチェックせず、アルゴリズムは HS、RS、ES までで、PS256 と EdDSA はありません。
ライセンス
MIT
確認したバージョン
4.6.2 ·
ソースコード
github.com/auth0/java-jwt
ドキュメント
github.com/auth0/java-jwt#readme

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512ES256ES384ES512

java-jwt は JWT.create()…sign(algorithm) でトークンを作成し、JWT.require(algorithm)…build().verify(token) で検証します。Algorithm オブジェクトはアルゴリズムと鍵の両方を保持するため、検証器はその 1 つのアルゴリズムしか受け入れません。RS256 トークン、署名のないトークン、EC 鍵の PEM テキストで偽造した HS256 トークンは、いずれも ES256 の検証器に対して AlgorithmMismatchException で失敗しました。

この保護が有効なのは、Algorithm を設定から選んでいる場合だけです。JWT.require(Algorithm.none()) は署名のないトークンを受け入れ、Algorithm.HMAC256(pemBytes) は偽造した HS256 トークンの検証を通しました。トークンの alg ヘッダーをもとに Algorithm を選ぶコードや、鍵を生のバイト列として読み込むコードは、本来この API が防いでいるアルゴリズム混同のバグを再び引き起こします。JWT.decode() は署名もクレームもチェックせずに解析し、1 時間前に期限切れになったトークンもそのまま返しました。

対応アルゴリズムは HMAC256/384/512、RSA256/384/512、ECDSA256/384/512 で、PS256、ES256K、EdDSA はありません。そのため、RSA-PSS や Ed25519 に移行した発行者のトークンには Nimbus か JJWT が必要です。ES256 は署名時に P-384 の鍵を拒否します。

ES256 の署名は 64 バイトの生の r‖s で、DER の署名は汎用的な The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA で失敗します。署名の長さは JWT デバッガーで確認できます。デフォルトでは時刻のずれを許容せず、exp も任意です。サンプルでは、発行者のチェックに加えて withClaimPresence("exp") と acceptLeeway(30) を指定しています。

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

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/java-jwt/run.sh がコンテナイメージ maven:3.9.16-eclipse-temurin-25 内で java-jwt (Auth0) 4.6.2 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    256/384/512 の HS、RS、ES で署名と検証ができました。Algorithm のファクトリーには PS*、ES256K、EdDSA がありません。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
    evidence jws-algorithms: Algorithm factory methods: HMAC256/384/512, RSA256/384/512, ECDSA256/384/512, none; no PS*, ES256K or EdDSA
  • ES256 署名のエンコーディング

    raw

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

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

    rejected

    DER エンコードの ES256 署名は SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA で失敗します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA
  • アルゴリズムの許可リスト

    required

    検証器は 1 つの Algorithm に固定されます。JWT.require(ECDSA256) に対して RS256 トークンを検証すると、AlgorithmMismatchException で失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: JWT.require(ECDSA256) on an RS256 token: AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
  • 署名なしトークン(alg none)

    opt-in

    ES256 の検証器は署名のないトークンを拒否しますが、JWT.require(Algorithm.none()) は受け入れます。

    チェックによる記録
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: JWT.require(ECDSA256): AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
    evidence jwt-alg-none: JWT.require(Algorithm.none()): verified
  • HMAC シークレットとして使われる公開鍵

    rejected

    PEM テキストを鍵とした HS256 トークンは、ES256 の検証器に対しては失敗しましたが、Algorithm.HMAC256(pemBytes) では検証を通りました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: JWT.require(ECDSA256): AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
    evidence jwt-hmac-public-key: JWT.require(Algorithm.HMAC256(PEM bytes)): verified
  • alg と曲線の照合

    enforced

    P-384 の鍵を渡した Algorithm.ECDSA256 は、署名時に SignatureGenerationException で失敗します。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: SignatureGenerationException: The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withECDSA
  • デフォルトのクロックスキュー

    0s

    デフォルトでは時刻のずれを許容しません。3 秒前に期限切れになったトークンは拒否されました。

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

    no

    exp のないトークンも検証を通りました。withClaimPresence("exp") を指定すると必須になります。

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

使い方

ES256 トークンに署名し、発行者を固定し、exp を必須にして、30 秒の時刻のずれを許容して検証します。
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.time.Instant;

public class Usage {
    public static void main(String[] args) throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
        generator.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair pair = generator.generateKeyPair();
        Algorithm es256 = Algorithm.ECDSA256((ECPublicKey) pair.getPublic(), (ECPrivateKey) pair.getPrivate());

        String token = JWT.create().withSubject("user-42").withIssuer("https://auth.example.com")
            .withExpiresAt(Instant.now().plusSeconds(600)).sign(es256);

        // JWT.require() verifies; JWT.decode() only parses. Require exp and allow some leeway.
        DecodedJWT verified = JWT.require(es256).withIssuer("https://auth.example.com")
            .withClaimPresence("exp").acceptLeeway(30).build().verify(token);
        System.out.println("sub: " + verified.getSubject());
    }
}

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

セットアップ
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
コマンド
java -cp 'lib/*' Usage.java
一致すべき出力(正規表現)
sub: user-42

ライブラリ