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

jose4j

Brian Campbell による jose4j は、Java で書かれた複数の ID サーバーを支える JOSE ライブラリです。JwtConsumer はデフォルトで none をブロックし、種類の合わない鍵を拒否しますが、制限しない限り、鍵で使えるそれ以外のアルゴリズムはすべて許可します。
ライセンス
Apache-2.0
確認したバージョン
0.9.7 ·
ソースコード
bitbucket.org/b_c/jose4j
ドキュメント
bitbucket.org/b_c/jose4j/wiki/Home

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

jose4j は JDK 自身のプロバイダーの上に JWS、JWE、JWK、JWT を実装しています。署名には JsonWebSignature を、検証とクレームのチェックには JwtConsumerBuilder で構築した JwtConsumer を使います。0.9.7 は 256/384/512 の HS、RS、PS、ES と EdDSA で署名と検証ができました。ES256K、Ed25519、Ed448 はアルゴリズム名として認識されません。

デフォルトでは古典的な 2 つの攻撃をブロックしますが、アルゴリズムは制限しません。署名を明示的に無効にしない限り none は拒否され('none' is a blocked algorithm.)、鍵の種類も一致している必要があります。EC 鍵に対する RS256 トークンと、EC 鍵の PEM テキストを鍵とした HS256 トークンは、どちらも失敗しました(cannot be used for HMAC verification)。ただし、鍵で実行できる他のアルゴリズムはすべて受け入れられるため、サンプルのように setJwsAlgorithmConstraints(ConstraintType.PERMIT, ...) でアルゴリズムを固定してください。PEM のバイト列から作成した HmacKey は、偽造した HS256 トークンの検証を通しました。鍵は必ず設定から取得し、トークンが示す内容から決めてはいけません。

鍵長は厳格にチェックされます。ES256 は P-384 の鍵を拒否し(expects a key using P-256 but was P-384)、RFC 7518 がハッシュの出力以上の長さの鍵を求めているため、HS256 は 16 バイトの鍵を拒否しました。ES256 の署名は 64 バイトの生の r‖s で、DER の署名は Invalid JWS Signature で失敗します。長さは JWT デバッガーで確認できます。

クレームのデフォルトは、厳しい方向と緩い方向に分かれています。時刻のずれをまったく許容しない点は多くのチームの想定より厳格で、exp を必須としない点は想定より緩やかです。別のサービスからのトークンを読み取るすべてのコンシューマーで、setRequireExpirationTime()、setExpectedIssuer()、setExpectedAudience()、setAllowedClockSkewInSeconds() を設定すべきです。

暗号化されたトークンについては、jose4j が生成した JWE の内容を JWE インスペクターで確認できます。

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

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

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    256/384/512 の HS、RS、PS、ES と EdDSA で署名と検証ができました。ES256K、Ed25519、Ed448 では InvalidAlgorithmException が発生しました。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: ES256K: InvalidAlgorithmException: ES256K is an unknown, unsupported or unavailable alg algorithm (not one of [none, HS256, HS384, HS512, EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512]).
    evidence jws-algorithms: Ed25519: InvalidAlgorithmException: Ed25519 is an unknown, unsupported or unavailable alg algorithm (not one of [none, HS256, HS384, HS512, EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512]).
    evidence jws-algorithms: Ed448: InvalidAlgorithmException: Ed448 is an unknown, unsupported or unavailable alg algorithm (not one of [none, HS256, HS384, HS512, EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512]).
  • ES256 署名のエンコーディング

    raw

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

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

    rejected

    DER エンコードの ES256 署名は InvalidJwtSignatureException: JWT rejected due to invalid signature で失敗します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJwtSignatureException: JWT rejected due to invalid signature. Additional details: [[9] Invalid JWS Signature:
  • アルゴリズムの許可リスト

    from-key

    アルゴリズムの制約がない場合は、鍵の種類で決まります。EC 鍵に対して RS256 トークンを検証すると、The given key (algorithm=EC) is not valid for SHA256withRSA で失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: JwtConsumer without algorithm constraints, RS256 token against an EC key: InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process JOSE object (cause: org.jose4j.lang.InvalidKeyException: The given key (algorithm=EC) is not valid for SHA256withRSA):
  • 署名なしトークン(alg none)

    opt-in

    デフォルトのコンシューマーは、署名のないトークンを 'none' is a blocked algorithm. で拒否します。アルゴリズムの制約なしで setDisableRequireSignature() を指定すると受け入れます。

    チェックによる記録
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: default JwtConsumer: InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process JOSE object (cause: org.jose4j.lang.InvalidAlgorithmException: 'none' is a blocked algorithm.):
    evidence jwt-alg-none: setDisableRequireSignature with no algorithm constraints: verified
  • HMAC シークレットとして使われる公開鍵

    rejected

    PEM テキストを鍵とした HS256 トークンは、EC 公開鍵に対しては失敗しましたが、PEM のバイト列から作成した HmacKey では検証を通りました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: JwtConsumer with the EC public key: InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process JOSE object (cause: org.jose4j.lang.InvalidKeyException: class sun.security.ec.ECPublicKeyImpl cannot be used for HMAC verification.):
    evidence jwt-hmac-public-key: JwtConsumer with an HmacKey built from the PEM bytes: verified
  • alg と曲線の照合

    enforced

    P-384 の鍵で ES256 の署名を行うと ES256/SHA256withECDSA expects a key using P-256 but was P-384 で失敗します。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidKeyException: ES256/SHA256withECDSA expects a key using P-256 but was P-384
  • デフォルトのクロックスキュー

    0s

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

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

    no

    exp のないトークンも受け入れられました。setRequireExpirationTime() を指定すると必須になります。

    チェックによる記録
    fact jwt-exp-required = no
  • 短い HMAC シークレット

    rejected

    HS256 は 16 バイトの鍵を拒否しました(A key of the same size as the hash output ... or larger MUST be used)。

    チェックによる記録
    fact jwt-hmac-short-secret = rejected
    evidence jwt-hmac-short-secret: HS256 with a 16-byte key: InvalidKeyException: A key of the same size as the hash output (i.e. 256 bits for HS256) or larger MUST be used with the HMAC SHA algorithms but this key is only 128 bits

使い方

生成した JWK で ES256 トークンに署名し、アルゴリズムを固定し、発行者と exp を必須にして、30 秒の時刻のずれを許容して読み取ります。
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.EcJwkGenerator;
import org.jose4j.jwk.EllipticCurveJsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.EllipticCurves;

public class Usage {
    public static void main(String[] args) throws Exception {
        EllipticCurveJsonWebKey key = EcJwkGenerator.generateJwk(EllipticCurves.P256);
        JwtClaims claims = new JwtClaims();
        claims.setSubject("user-42");
        claims.setIssuer("https://auth.example.com");
        claims.setExpirationTimeMinutesInTheFuture(10);

        JsonWebSignature jws = new JsonWebSignature();
        jws.setPayload(claims.toJson());
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);
        jws.setKey(key.getPrivateKey());

        // Pin the algorithm and require exp; the defaults would allow any non-'none' alg for the key.
        JwtConsumer consumer = new JwtConsumerBuilder()
            .setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256)
            .setVerificationKey(key.getPublicKey())
            .setExpectedIssuer("https://auth.example.com")
            .setRequireExpirationTime()
            .setAllowedClockSkewInSeconds(30)
            .build();
        System.out.println("sub: " + consumer.processToClaims(jws.getCompactSerialization()).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

ライブラリ