Saltar al contenido
ecdsa.com
Java · Kotlin

jose4j

jose4j, de Brian Campbell, la biblioteca JOSE en la que se apoyan varios servidores de identidad Java: un JwtConsumer que por defecto bloquea none y rechaza los tipos de clave que no corresponden, pero admite cualquier otro algoritmo para una clave salvo que lo restrinjas.
Licencia
Apache-2.0
Versión comprobada
0.9.7 ·
Código fuente
bitbucket.org/b_c/jose4j
Documentación
bitbucket.org/b_c/jose4j/wiki/Home

Compatibilidad de un vistazo

JOSE y JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

jose4j implementa JWS, JWE, JWK y JWT sobre los propios proveedores del JDK, con JsonWebSignature para firmar y un JwtConsumer creado con JwtConsumerBuilder para la verificación y la comprobación de los claims. La 0.9.7 superó la prueba de ida y vuelta con HS, RS, PS y ES de 256/384/512 y EdDSA; ES256K, Ed25519 y Ed448 no son nombres de algoritmo reconocidos.

Los valores predeterminados bloquean los dos ataques clásicos, pero dejan abierto el algoritmo. none se rechaza ('none' is a blocked algorithm.) salvo que las firmas se desactiven explícitamente, y el tipo de clave tiene que encajar: un token RS256 contra una clave EC y un token HS256 con el texto PEM de la clave EC como clave fallaron ambos (cannot be used for HMAC verification). Sin embargo, se acepta cualquier otro algoritmo que la clave pueda ejecutar, así que fíjalo con setJwsAlgorithmConstraints(ConstraintType.PERMIT, ...), como hace el ejemplo. Un HmacKey construido a partir de los bytes PEM verificó el token HS256 falsificado: las claves deben salir de la configuración, nunca de lo que afirma el token.

Los tamaños de clave se validan: ES256 rechaza una clave P-384 (expects a key using P-256 but was P-384) y HS256 rechazó una clave de 16 bytes porque el RFC 7518 exige claves al menos tan largas como la salida del hash. Las firmas ES256 son el r‖s en bruto de 64 bytes; una firma DER falla con Invalid JWS Signature, y el decodificador de JWT muestra la longitud.

Los valores predeterminados de los claims tiran en sentidos opuestos: la ausencia de tolerancia de desfase de reloj es más estricta de lo que espera la mayoría de los equipos, y la ausencia de exp obligatorio, más laxa. setRequireExpirationTime(), setExpectedIssuer(), setExpectedAudience() y setAllowedClockSkewInSeconds() deben estar en todo consumidor que lea tokens de otro servicio.

Para los tokens cifrados, el inspector de JWE muestra qué contiene un JWE generado con jose4j.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/jose4j/run.sh ejecutó jose4j 0.9.7 en la imagen de contenedor maven:3.9.16-eclipse-temurin-25 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Con HS, RS, PS y ES de 256/384/512 y EdDSA se firmó y se verificó; ES256K, Ed25519 y Ed448 lanzaron InvalidAlgorithmException.

    Registrado por la comprobación
    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]).
  • Codificación de la firma ES256

    raw

    Las firmas ES256 son 64 bytes de r‖s en bruto.

    Registrado por la comprobación
    fact jws-ecdsa-encoding = raw
  • Firma DER dentro de un JWS

    rejected

    Una firma ES256 codificada en DER falla con InvalidJwtSignatureException: JWT rejected due to invalid signature.

    Registrado por la comprobación
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJwtSignatureException: JWT rejected due to invalid signature. Additional details: [[9] Invalid JWS Signature:
  • Lista de algoritmos permitidos

    from-key

    Sin restricciones de algoritmo, decide el tipo de clave: un token RS256 contra una clave EC falló con The given key (algorithm=EC) is not valid for SHA256withRSA.

    Registrado por la comprobación
    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):
  • Tokens sin firma (alg none)

    opt-in

    El consumidor predeterminado rechaza los tokens sin firmar con 'none' is a blocked algorithm.; setDisableRequireSignature() sin restricciones de algoritmo los acepta.

    Registrado por la comprobación
    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
  • Clave pública usada como secreto HMAC

    rejected

    Un token HS256 con el texto PEM como clave falló contra la clave pública EC, pero un HmacKey construido a partir de los bytes PEM lo verificó.

    Registrado por la comprobación
    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
  • Curva cotejada con alg

    enforced

    Firmar ES256 con una clave P-384 falla con ES256/SHA256withECDSA expects a key using P-256 but was P-384.

    Registrado por la comprobación
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidKeyException: ES256/SHA256withECDSA expects a key using P-256 but was P-384
  • Desfase de reloj predeterminado

    0s

    Sin tolerancia de desfase de reloj por defecto: se rechazó un token caducado 3 segundos antes.

    Registrado por la comprobación
    fact jwt-clock-skew = 0s
  • Claim de caducidad obligatorio

    no

    Se aceptó un token sin exp; setRequireExpirationTime() lo exige.

    Registrado por la comprobación
    fact jwt-exp-required = no
  • Secreto HMAC corto

    rejected

    HS256 rechazó una clave de 16 bytes: A key of the same size as the hash output ... or larger MUST be used.

    Registrado por la comprobación
    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

Uso

Firma un token ES256 con una JWK generada y consúmelo con el algoritmo fijado, el emisor y exp obligatorios y 30 segundos de tolerancia de reloj.
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());
    }
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor maven:3.9.16-eclipse-temurin-25, y la ejecución falla si la salida no coincide.

Configuración
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Comando
java -cp 'lib/*' Usage.java
La salida debe coincidir (expresión regular)
sub: user-42

Bibliotecas