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

Nimbus JOSE + JWT

Connect2id の Nimbus JOSE + JWT は、Spring Security や多くの Java の OAuth サーバーの内部で使われている JOSE ライブラリです。プロセッサーでは鍵セレクターがアルゴリズムの許可リストとなり、署名のないトークンは拒否され、時刻のずれはデフォルトで 60 秒まで許容されます。
ライセンス
Apache-2.0
確認したバージョン
10.10 ·
ソースコード
bitbucket.org/connect2id/nimbus-jose-jwt
ドキュメント
connect2id.com/products/nimbus-jose-jwt

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSAEd25519

Nimbus は JWS、JWE、JWK、JWT を 2 つのレイヤーで扱います。低レベルのオブジェクト(SignedJWT、ECDSASigner、MACVerifier)と、鍵の選択、署名のチェック、クレームのチェックをまとめる DefaultJWTProcessor です。Spring Security のリソースサーバーはこの上に構築されているため、そのデフォルトがほとんどの Spring の API で適用されるルールになります。

プロセッサーを使う場合は、JWSVerificationKeySelector が許可リストになります。ES256 用に設定したセレクターは、RS256 トークンと、EC 鍵の PEM テキストを鍵とした HS256 トークンを Another algorithm expected, or no matching key(s) found で拒否しました。セレクターがまったくない場合は、すべてを拒否します。署名のないトークンは拒否されます(Unsecured (plain) JWTs are rejected, extend class to handle)。低レベルの API にはこうした保護がありません。PEM テキストから作成した MACVerifier は、その偽造された HS256 トークンの検証を通しました。そのため、トークンのヘッダーに従って検証器を選ぶコードは、古典的なアルゴリズム混同のバグを再現してしまいます。サンプルのように、プロセッサーを使ってください。

DefaultJWTClaimsVerifier はデフォルトで 60 秒の時刻のずれを許容し、exp は必須クレームとして指定しない限り任意です。サンプルでは sub と exp を必須にし、発行者を固定しています。ES256 は P-384 の鍵を拒否し(The ES256 algorithm is not allowed or supported by the JWS signer)、DER エンコードの署名は単に検証に失敗します。署名の長さは JWT デバッガーで確認してください。

バージョン 10.10 では、HS、RS、PS、ES の各ファミリーに加え、EdDSA と新しい名前の Ed25519 でも、署名と検証を往復で確認できました。Ed25519 にはクラスパス上の Google Tink が必要で、ない場合は署名が NoClassDefFoundError: com/google/crypto/tink/subtle/Ed25519Sign$KeyPair で失敗しました。JDK には secp256k1 がないため、ES256K には Bouncy Castle のような secp256k1 を備えたプロバイダーが必要です。

ID プロバイダーが公開している内容は JWKS チェッカーで、暗号化されたトークンは JWE インスペクターで確認してください。

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

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

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSAEd25519

    JDK 上で、256/384/512 の HS、RS、PS、ES と EdDSA、Ed25519 で署名と検証ができました。ES256K と Ed448 は、JDK のプロバイダーでは署名から検証までを完了できませんでした。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA, Ed25519
    evidence jws-algorithms: ES256K: not round-tripped with the JDK providers
    evidence jws-algorithms: Ed448: not round-tripped with the JDK providers
  • ES256 署名のエンコーディング

    raw

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

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

    rejected

    DER エンコードの ES256 署名を持つトークンは検証に失敗しました。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: not verified
  • アルゴリズムの許可リスト

    required

    DefaultJWTProcessor には鍵セレクターが必要です。ない場合は No JWS key selector is configured で失敗し、ES256 用のセレクターは RS256 トークンを拒否しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: DefaultJWTProcessor with a JWSVerificationKeySelector for ES256, RS256 token: BadJOSEException: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
    evidence jwt-algorithm-allowlist: DefaultJWTProcessor without a key selector: BadJOSEException: Signed JWT rejected: No JWS key selector is configured
  • 署名なしトークン(alg none)

    rejected

    プロセッサーは、署名のないトークンを Unsecured (plain) JWTs are rejected, extend class to handle で拒否します。

    チェックによる記録
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: DefaultJWTProcessor: BadJOSEException: Unsecured (plain) JWTs are rejected, extend class to handle
    evidence jwt-alg-none: SignedJWT.parse: ParseException: Invalid JWS header: Not a JWS header
  • HMAC シークレットとして使われる公開鍵

    rejected

    PEM テキストを鍵とした HS256 トークンは、ES256 用に設定したプロセッサーでは拒否されましたが、同じ PEM テキストから手作業で作成した MACVerifier では検証を通りました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: DefaultJWTProcessor with the ES256 key selector: BadJOSEException: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
    evidence jwt-hmac-public-key: a MACVerifier built from the PEM text by hand: verified
  • alg と曲線の照合

    enforced

    P-384 の鍵で ES256 の署名を行うと、The ES256 algorithm is not allowed or supported by the JWS signer: Supported algorithms: [ES384] で失敗します。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: JOSEException: The ES256 algorithm is not allowed or supported by the JWS signer: Supported algorithms: [ES384]
  • デフォルトのクロックスキュー

    60s

    DefaultJWTClaimsVerifier は 60 秒の時刻のずれを許容します(DEFAULT_MAX_CLOCK_SKEW_SECONDS)。

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

    no

    exp のないトークンは、デフォルトのクレーム検証を通過しました。exp を必須として指定すると、JWT missing required claims: [exp] で失敗しました。

    チェックによる記録
    fact jwt-exp-required = no
    evidence jwt-exp-required: with DefaultJWTClaimsVerifier requiring exp: BadJWTException: JWT missing required claims: [exp]
  • Google Tink を使わない Ed25519

    fails

    クラスパス上に Google Tink がない場合、Ed25519Signer による署名は NoClassDefFoundError: com/google/crypto/tink/subtle/Ed25519Sign$KeyPair で失敗しました。

    チェックによる記録
    fact ed25519-without-tink = fails
    evidence ed25519-without-tink: NoClassDefFoundError: com/google/crypto/tink/subtle/Ed25519Sign$KeyPair

使い方

鍵 ID を付けて ES256 トークンに署名し、ES256 専用の鍵セレクターと、sub、exp、発行者を必須とするクレーム検証器で処理します。
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.proc.*;
import com.nimbusds.jwt.*;
import com.nimbusds.jwt.proc.*;
import java.util.*;

public class Usage {
    public static void main(String[] args) throws Exception {
        ECKey key = new ECKeyGenerator(Curve.P_256).keyID("2026-10").generate();
        JWTClaimsSet claims = new JWTClaimsSet.Builder().subject("user-42").issuer("https://auth.example.com")
            .expirationTime(new Date(System.currentTimeMillis() + 600_000)).build();
        SignedJWT jwt = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(key.getKeyID()).build(), claims);
        jwt.sign(new ECDSASigner(key));

        // The key selector is the algorithm allow-list; the claims verifier requires iss and exp.
        DefaultJWTProcessor<SecurityContext> processor = new DefaultJWTProcessor<>();
        processor.setJWSKeySelector(new JWSVerificationKeySelector<>(JWSAlgorithm.ES256, new ImmutableJWKSet<>(new JWKSet(key.toPublicJWK()))));
        processor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(
            new JWTClaimsSet.Builder().issuer("https://auth.example.com").build(), new HashSet<>(List.of("sub", "exp"))));
        System.out.println("sub: " + processor.process(jwt.serialize(), null).getSubject());
    }
}

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

セットアップ
mvn -q -B dependency:copy -Dartifact=com.nimbusds:nimbus-jose-jwt:10.10 -DoutputDirectory=lib >/dev/null
コマンド
java -cp lib/nimbus-jose-jwt-10.10.jar Usage.java
一致すべき出力(正規表現)
sub: user-42

ライブラリ