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

jose4j

O jose4j, de Brian Campbell, a biblioteca JOSE por trás de vários servidores de identidade Java: um JwtConsumer que, por padrão, bloqueia none e recusa tipos de chave incompatíveis, mas aceita qualquer outro algoritmo compatível com a chave, a menos que você restrinja os algoritmos.
Licença
Apache-2.0
Versão verificada
0.9.7 ·
Código-fonte
bitbucket.org/b_c/jose4j
Documentação
bitbucket.org/b_c/jose4j/wiki/Home

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

O jose4j implementa JWS, JWE, JWK e JWT sobre os próprios provedores do JDK, com JsonWebSignature para assinar e um JwtConsumer, criado por JwtConsumerBuilder, para a verificação e a validação das claims. A versão 0.9.7 fez o ciclo completo de ida e volta com HS, RS, PS e ES em 256/384/512 e com EdDSA; ES256K, Ed25519 e Ed448 não são nomes de algoritmo reconhecidos.

Os padrões bloqueiam os dois ataques clássicos, mas deixam o algoritmo em aberto. none é recusado ('none' is a blocked algorithm.), a menos que as assinaturas sejam desativadas explicitamente, e o tipo da chave precisa ser compatível: um token RS256 verificado com uma chave EC e um token HS256 que usava como chave o texto PEM da chave EC falharam (cannot be used for HMAC verification). Já qualquer outro algoritmo que a chave consiga executar é aceito; por isso, fixe o algoritmo com setJwsAlgorithmConstraints(ConstraintType.PERMIT, ...), como faz o exemplo. Uma HmacKey criada a partir dos bytes PEM verificou o token HS256 forjado — as chaves devem vir da configuração, nunca do que o token declara.

A biblioteca impõe os tamanhos de chave: o ES256 recusa uma chave P-384 (expects a key using P-256 but was P-384), e o HS256 recusou uma chave de 16 bytes porque a RFC 7518 exige chaves pelo menos do tamanho da saída do hash. As assinaturas ES256 são o r‖s bruto de 64 bytes; uma assinatura DER falha com Invalid JWS Signature — o depurador de JWT mostra o tamanho.

Os padrões das claims apontam em direções opostas: não ter tolerância para diferença de relógio é mais estrito do que a maioria das equipes espera, e não exigir exp é mais permissivo. setRequireExpirationTime(), setExpectedIssuer(), setExpectedAudience() e setAllowedClockSkewInSeconds() devem constar em todo consumidor que lê tokens de outro serviço.

Para tokens criptografados, o inspetor de JWE mostra o que contém um JWE produzido pelo jose4j.

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

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/jose4j/run.sh executou jose4j 0.9.7 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

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Assinatura e verificação funcionaram com HS, RS, PS e ES em 256/384/512 e com EdDSA; ES256K, Ed25519 e Ed448 lançaram InvalidAlgorithmException.

    Registrado pela verificação
    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]).
  • 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 InvalidJwtSignatureException: JWT rejected due to invalid signature.

    Registrado pela verificação
    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

    Sem restrições de algoritmo, quem decide é o tipo da chave: um token RS256 verificado com uma chave EC falhou com The given key (algorithm=EC) is not valid for SHA256withRSA.

    Registrado pela verificação
    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 sem assinatura (alg none)

    opt-in

    O consumidor padrão recusa tokens sem assinatura com 'none' is a blocked algorithm.; com setDisableRequireSignature() e sem restrições de algoritmo, eles são aceitos.

    Registrado pela verificação
    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
  • Chave pública usada como segredo HMAC

    rejected

    Um token HS256 que usava o texto PEM como chave falhou contra a chave pública EC, mas uma HmacKey criada a partir dos bytes PEM o verificou.

    Registrado pela verificação
    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 conferida com o alg

    enforced

    Assinar ES256 com uma chave P-384 falha com ES256/SHA256withECDSA expects a key using P-256 but was P-384.

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidKeyException: ES256/SHA256withECDSA expects a key using P-256 but was P-384
  • Tolerância padrão de diferença de relógio

    0s

    Sem tolerância para diferença de relógio 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 aceito; setRequireExpirationTime() torna essa claim obrigatória.

    Registrado pela verificação
    fact jwt-exp-required = no
  • Segredo HMAC curto

    rejected

    O HS256 recusou uma chave de 16 bytes: A key of the same size as the hash output ... or larger MUST be used.

    Registrado pela verificação
    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

Assine um token ES256 com uma JWK gerada e processe-o com o algoritmo fixado, emissor e exp obrigatórios e 30 segundos de tolerância para diferença de relógio.
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());
    }
}

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