Saltar al contenido
ecdsa.com
Java · Kotlin

JJWT (io.jsonwebtoken)

JJWT, de Les Hazlewood, una biblioteca JWT muy usada en Java y Android: la clave de verificación fija el algoritmo aceptable, los tokens sin firmar necesitan un analizador unsecured() explícito y las claves HMAC débiles se rechazan directamente.
Licencia
Apache-2.0
Versión comprobada
0.13.0 ·
Código fuente
github.com/jwtk/jjwt
Documentación
github.com/jwtk/jjwt#readme

Compatibilidad de un vistazo

JOSE y JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

JJWT 0.12 y posteriores ofrecen un constructor y un analizador fluidos —Jwts.builder()…signWith(key, alg) y Jwts.parser().verifyWith(key)…parseSignedClaims(token)—, con los algoritmos en Jwts.SIG. Se distribuye en tres artefactos: jjwt-api en tiempo de compilación, y jjwt-impl y un módulo de integración JSON como jjwt-jackson en tiempo de ejecución. La 0.13.0 superó la prueba de ida y vuelta con HS, RS, PS y ES de 256/384/512 y EdDSA; ES256K y los nombres Ed25519 y Ed448 no están en Jwts.SIG.

El analizador deduce el algoritmo permitido a partir de la clave de verificación, y sus errores lo dicen claramente. Un token RS256 contra una clave pública EC y un token HS256 con el texto PEM de la clave EC como clave fallaron ambos con UnsupportedJwtException, que explica que la clave no se puede usar con ese algoritmo. La protección es el tipo de clave, no una lista: un analizador con Keys.hmacShaKeyFor(pemBytes) verificó ese token falsificado, así que nunca construyas claves HMAC a partir de material que pueda ser público.

parseSignedClaims rechaza los tokens sin firmar con un mensaje que cita el RFC 7518 §3.6, y solo se pueden leer mediante Jwts.parser().unsecured()…parseUnsecuredClaims. Las claves HMAC débiles ni siquiera llegan tan lejos: Keys.hmacShaKeyFor rechazó una clave de 16 bytes con WeakKeyException, porque el RFC 7518 exige claves al menos tan largas como el hash. ES256 rechaza una clave P-384 al firmar.

Hay dos valores predeterminados que merecen atención. No hay tolerancia para el desfase de reloj —un token caducado 3 segundos antes falló— y exp es opcional, así que establece clockSkewSeconds y comprueba la caducidad y el emisor como hace el ejemplo. Las firmas ES256 son el r‖s en bruto de 64 bytes; una firma DER se notifica con precisión (Provided signature is 576 bits (72 bytes) but ES256 signatures must be exactly 512 bits (64 bytes)): es una de las pocas bibliotecas que nombran el problema de codificación. Inspecciona los tokens en el decodificador de JWT y las claves HMAC con el comprobador de secretos JWT.

Para manejar JWE y conjuntos JWK a gran escala, compárala con Nimbus JOSE + JWT.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/jjwt/run.sh ejecutó JJWT (io.jsonwebtoken) 0.13.0 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 no están en Jwts.SIG.

    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: not in Jwts.SIG
    evidence jws-algorithms: Ed25519: not in Jwts.SIG
    evidence jws-algorithms: Ed448: not in Jwts.SIG
  • 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 Provided signature is 576 bits (72 bytes) but ES256 signatures must be exactly 512 bits (64 bytes).

    Registrado por la comprobación
    fact jws-der-signature = rejected
    evidence jws-der-signature: SignatureException: Unable to verify Elliptic Curve signature using provided ECPublicKey: Provided signature is 576 bits (72 bytes) but ES256 signatures must be exactly 512 bits (64 bytes) per [RFC 7518, Section 3.4 (validation)](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).
  • Lista de algoritmos permitidos

    from-key

    La clave de verificación determina el algoritmo: un token RS256 contra una clave pública EC falló con UnsupportedJwtException.

    Registrado por la comprobación
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: RS256 token, verifyWith(EC public key): UnsupportedJwtException: The parsed JWT indicates it was signed with the 'RS256' signature algorithm, but the provided sun.security.ec.ECPublicKeyImpl key may not be used to verify RS256 signatures.  Because the specified key reflects a specific and expected algorithm, and the JWT does not reflect this algorithm, it is likely that the JWT was not expected and therefore should not be trusted.  Another possibility is that the parser was provided the incorrect signature verification key, but this cannot be assumed for security reasons.
  • Tokens sin firma (alg none)

    opt-in

    parseSignedClaims rechaza los tokens sin firmar; Jwts.parser().unsecured() con parseUnsecuredClaims los lee.

    Registrado por la comprobación
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: parseSignedClaims with the EC key: UnsupportedJwtException: Unsecured JWSs (those with an 'alg' (Algorithm) header value of 'none') are disallowed by default as mandated by https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6. If you wish to allow them to be parsed, call the JwtParserBuilder.unsecured() method, but please read the security considerations covered in that method's JavaDoc before doing so. Header: {alg=none, typ=JWT}
    evidence jwt-alg-none: parser().unsecured().parseUnsecuredClaims: verified
  • Clave pública usada como secreto HMAC

    rejected

    Un token HS256 con el texto PEM como clave se rechazó contra la clave pública EC, pero se verificó con Keys.hmacShaKeyFor(pemBytes).

    Registrado por la comprobación
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: verifyWith(EC public key): UnsupportedJwtException: The parsed JWT indicates it was signed with the 'HS256' signature algorithm, but the provided sun.security.ec.ECPublicKeyImpl key may not be used to verify HS256 signatures.  Because the specified key reflects a specific and expected algorithm, and the JWT does not reflect this algorithm, it is likely that the JWT was not expected and therefore should not be trusted.  Another possibility is that the parser was provided the incorrect signature verification key, but this cannot be assumed for security reasons.
    evidence jwt-hmac-public-key: verifyWith(Keys.hmacShaKeyFor(PEM bytes)): verified
  • Curva cotejada con alg

    enforced

    Firmar ES256 con una clave P-384 falla: the 'ES256' algorithm requires EC Keys with 256 bits.

    Registrado por la comprobación
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: SignatureException: Unable to compute ES256 signature. Cause: The provided Elliptic Curve signing key size (aka order bit length) is 384 bits (48 bytes), but the 'ES256' algorithm requires EC Keys with 256 bits (32 bytes) per [RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).
  • 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

    Un token sin exp se analizó correctamente.

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

    rejected

    Keys.hmacShaKeyFor rechazó una clave de 16 bytes con WeakKeyException.

    Registrado por la comprobación
    fact jwt-hmac-short-secret = rejected
    evidence jwt-hmac-short-secret: Keys.hmacShaKeyFor(16 bytes): WeakKeyException: The specified key byte array is 128 bits which is not secure enough for any JWT HMAC-SHA algorithm.  The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size).  Consider using the Jwts.SIG.HS256.key() builder (or HS384.key() or HS512.key()) to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm.  See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.

Uso

Emite un token ES256 y analízalo con la clave, el emisor obligatorio y 30 segundos de tolerancia de reloj.
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import java.security.KeyPair;
import java.util.Date;

public class Usage {
    public static void main(String[] args) {
        KeyPair pair = Jwts.SIG.ES256.keyPair().build();
        String token = Jwts.builder()
            .subject("user-42")
            .issuer("https://auth.example.com")
            .expiration(new Date(System.currentTimeMillis() + 600_000))
            .signWith(pair.getPrivate(), Jwts.SIG.ES256)
            .compact();

        // The key decides which algorithms are acceptable; require iss and exp explicitly
        // and allow some clock skew (the default is none).
        Claims claims = Jwts.parser()
            .verifyWith(pair.getPublic())
            .requireIssuer("https://auth.example.com")
            .clockSkewSeconds(30)
            .build()
            .parseSignedClaims(token)
            .getPayload();
        System.out.println("sub: " + claims.getSubject() + ", has exp: " + (claims.getExpiration() != null));
    }
}

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, has exp: true

Bibliotecas