Skip to content
ecdsa.com
Java · Kotlin

jose4j

Brian Campbell's jose4j, the JOSE library behind several Java identity servers: a JwtConsumer that blocks none and refuses mismatched key types by default, but allows any other algorithm for a key unless you constrain it.
License
Apache-2.0
Checked version
0.9.7 ·
Source
bitbucket.org/b_c/jose4j
Documentation
bitbucket.org/b_c/jose4j/wiki/Home

Compatibility at a glance

JOSE and JWT

JWS algorithms
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

jose4j implements JWS, JWE, JWK and JWT on the JDK's own providers, with JsonWebSignature for signing and a JwtConsumer built by JwtConsumerBuilder for verification and claims checks. 0.9.7 round-tripped HS, RS, PS and ES at 256/384/512 and EdDSA; ES256K, Ed25519 and Ed448 are not recognised algorithm names.

The defaults block the two classic attacks but leave the algorithm open. none is refused ('none' is a blocked algorithm.) unless signatures are switched off explicitly, and the key type has to fit: an RS256 token against an EC key and an HS256 token keyed with the EC key's PEM text both failed (cannot be used for HMAC verification). Any other algorithm the key can perform is accepted, though, so pin it with setJwsAlgorithmConstraints(ConstraintType.PERMIT, ...), as the sample does. An HmacKey built from the PEM bytes verified the forged HS256 token — keys must come from configuration, never from what the token claims.

Key sizes are enforced: ES256 refuses a P-384 key (expects a key using P-256 but was P-384) and HS256 refused a 16-byte key because RFC 7518 requires keys at least as long as the hash output. ES256 signatures are the 64-byte raw r‖s; a DER signature fails with Invalid JWS Signature — the JWT debugger shows the length.

Claims defaults cut both ways: no clock skew allowance is stricter than most teams expect, and no required exp is looser. setRequireExpirationTime(), setExpectedIssuer(), setExpectedAudience() and setAllowedClockSkewInSeconds() belong in every consumer that reads tokens from another service.

For encrypted tokens, the JWE inspector shows what a jose4j-produced JWE contains.

Facts and the checks behind them

Nothing below is copied from documentation. The check content/libraries/verify/jose4j/run.sh ran jose4j 0.9.7 in the container image maven:3.9.16-eclipse-temurin-25 on October 20, 2026; the page must match what it printed, and each fact lists those lines.

  • JWS algorithms

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    HS, RS, PS and ES at 256/384/512 and EdDSA signed and verified; ES256K, Ed25519 and Ed448 raised InvalidAlgorithmException.

    Recorded by the check
    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 signature encoding

    raw

    ES256 signatures are 64 bytes of raw r‖s.

    Recorded by the check
    fact jws-ecdsa-encoding = raw
  • DER signature inside a JWS

    rejected

    A DER-encoded ES256 signature fails with InvalidJwtSignatureException: JWT rejected due to invalid signature.

    Recorded by the check
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJwtSignatureException: JWT rejected due to invalid signature. Additional details: [[9] Invalid JWS Signature:
  • Algorithm allow-list

    from-key

    Without algorithm constraints the key type decides: an RS256 token against an EC key failed with The given key (algorithm=EC) is not valid for SHA256withRSA.

    Recorded by the check
    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):
  • Unsigned tokens (alg none)

    opt-in

    The default consumer refuses unsigned tokens with 'none' is a blocked algorithm.; setDisableRequireSignature() with no algorithm constraints accepts them.

    Recorded by the check
    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
  • Public key used as an HMAC secret

    rejected

    An HS256 token keyed with the PEM text failed against the EC public key, but an HmacKey built from the PEM bytes verified it.

    Recorded by the check
    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
  • Curve checked against alg

    enforced

    Signing ES256 with a P-384 key fails with ES256/SHA256withECDSA expects a key using P-256 but was P-384.

    Recorded by the check
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidKeyException: ES256/SHA256withECDSA expects a key using P-256 but was P-384
  • Default clock skew

    0s

    No clock skew allowance by default: a token expired three seconds earlier was rejected.

    Recorded by the check
    fact jwt-clock-skew = 0s
  • Expiry claim required

    no

    A token without exp was accepted; setRequireExpirationTime() requires it.

    Recorded by the check
    fact jwt-exp-required = no
  • Short HMAC secret

    rejected

    HS256 refused a 16-byte key: A key of the same size as the hash output ... or larger MUST be used.

    Recorded by the check
    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

Usage

Sign an ES256 token from a generated JWK and consume it with the algorithm pinned, issuer and exp required and 30 seconds of skew.
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());
    }
}

The snippet harness runs every sample verbatim in the container image maven:3.9.16-eclipse-temurin-25, and the run fails unless the output matches.

Setup
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Command
java -cp 'lib/*' Usage.java
Output must match (regular expression)
sub: user-42

Libraries