Pular para o conteúdo
ecdsa.com
Java · Kotlin

java-jwt (Auth0)

O java-jwt da Auth0: uma biblioteca JWT compacta em que cada verificador fica vinculado a um único objeto Algorithm, JWT.decode() não verifica nada e o conjunto de algoritmos para em HS, RS e ES — sem PS256 e sem EdDSA.
Licença
MIT
Versão verificada
4.6.2 ·
Código-fonte
github.com/auth0/java-jwt
Documentação
github.com/auth0/java-jwt#readme

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512ES256ES384ES512

O java-jwt cria tokens com JWT.create()…sign(algorithm) e os verifica com JWT.require(algorithm)…build().verify(token). O objeto Algorithm carrega tanto o algoritmo quanto a chave, então um verificador só aceita aquele único algoritmo: um token RS256, um token sem assinatura e um token HS256 forjado com o texto PEM da chave EC falharam contra um verificador ES256 com AlgorithmMismatchException.

A proteção só vale enquanto o Algorithm for escolhido a partir da configuração. JWT.require(Algorithm.none()) aceita tokens sem assinatura, e Algorithm.HMAC256(pemBytes) verificou o token HS256 forjado — um código que escolhe o Algorithm a partir do cabeçalho alg do token, ou que carrega chaves como bytes brutos, recria os bugs de confusão de algoritmo que a API normalmente evita. JWT.decode() analisa o token sem verificar a assinatura nem nenhuma claim; ele retornou um token que tinha expirado uma hora antes.

O conjunto de algoritmos é HMAC256/384/512, RSA256/384/512 e ECDSA256/384/512 — sem PS256, sem ES256K e sem EdDSA —, então tokens de emissores que migraram para RSA-PSS ou Ed25519 precisam do Nimbus ou do JJWT. O ES256 recusa uma chave P-384 no momento da assinatura.

As assinaturas ES256 são o r‖s bruto de 64 bytes, e uma assinatura DER falha com a mensagem genérica The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA; o depurador de JWT mostra o tamanho da assinatura. Não há tolerância padrão e exp é opcional: o exemplo acrescenta withClaimPresence("exp") e acceptLeeway(30), além da verificação do emissor.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/java-jwt/run.sh executou java-jwt (Auth0) 4.6.2 na imagem de contêiner maven:3.9.16-eclipse-temurin-25 em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    Assinatura e verificação funcionaram com HS, RS e ES em 256/384/512; a fábrica Algorithm não oferece PS*, ES256K nem EdDSA.

    Registrado pela verificação
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
    evidence jws-algorithms: Algorithm factory methods: HMAC256/384/512, RSA256/384/512, ECDSA256/384/512, none; no PS*, ES256K or EdDSA
  • Codificação da assinatura ES256

    raw

    As assinaturas ES256 são 64 bytes de r‖s brutos.

    Registrado pela verificação
    fact jws-ecdsa-encoding = raw
  • Assinatura DER dentro de um JWS

    rejected

    Uma assinatura ES256 codificada em DER falha com SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA.

    Registrado pela verificação
    fact jws-der-signature = rejected
    evidence jws-der-signature: SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA
  • Lista de algoritmos permitidos

    required

    Um verificador fica vinculado a um único Algorithm: um token RS256 verificado com JWT.require(ECDSA256) falhou com AlgorithmMismatchException.

    Registrado pela verificação
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: JWT.require(ECDSA256) on an RS256 token: AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
  • Tokens sem assinatura (alg none)

    opt-in

    Um verificador ES256 rejeita tokens sem assinatura; JWT.require(Algorithm.none()) os aceita.

    Registrado pela verificação
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: JWT.require(ECDSA256): AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
    evidence jwt-alg-none: JWT.require(Algorithm.none()): verified
  • Chave pública usada como segredo HMAC

    rejected

    Um token HS256 que usava o texto PEM como chave falhou contra o verificador ES256, mas Algorithm.HMAC256(pemBytes) o verificou.

    Registrado pela verificação
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: JWT.require(ECDSA256): AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
    evidence jwt-hmac-public-key: JWT.require(Algorithm.HMAC256(PEM bytes)): verified
  • Curva conferida com o alg

    enforced

    Algorithm.ECDSA256 com uma chave P-384 não consegue assinar e lança SignatureGenerationException.

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: SignatureGenerationException: The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withECDSA
  • Tolerância padrão de diferença de relógio

    0s

    Sem tolerância por padrão: um token expirado três segundos antes foi rejeitado.

    Registrado pela verificação
    fact jwt-clock-skew = 0s
  • Claim de expiração obrigatória

    no

    Um token sem exp foi verificado; withClaimPresence("exp") passa a exigir essa claim.

    Registrado pela verificação
    fact jwt-exp-required = no

Uso

Assine um token ES256 e verifique-o com o emissor fixado, exp obrigatória e 30 segundos de tolerância.
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.time.Instant;

public class Usage {
    public static void main(String[] args) throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
        generator.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair pair = generator.generateKeyPair();
        Algorithm es256 = Algorithm.ECDSA256((ECPublicKey) pair.getPublic(), (ECPrivateKey) pair.getPrivate());

        String token = JWT.create().withSubject("user-42").withIssuer("https://auth.example.com")
            .withExpiresAt(Instant.now().plusSeconds(600)).sign(es256);

        // JWT.require() verifies; JWT.decode() only parses. Require exp and allow some leeway.
        DecodedJWT verified = JWT.require(es256).withIssuer("https://auth.example.com")
            .withClaimPresence("exp").acceptLeeway(30).build().verify(token);
        System.out.println("sub: " + verified.getSubject());
    }
}

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner maven:3.9.16-eclipse-temurin-25, e a execução falha se a saída não corresponder.

Configuração
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Comando
java -cp 'lib/*' Usage.java
A saída deve corresponder a (expressão regular)
sub: user-42

Bibliotecas