Aller au contenu
ecdsa.com
Java · Kotlin

Nimbus JOSE + JWT

Nimbus JOSE + JWT de Connect2id, la bibliothèque JOSE au cœur de Spring Security et de nombreux serveurs OAuth Java : un processeur dont le sélecteur de clés sert de liste d’autorisation des algorithmes, des jetons non signés refusés et 60 secondes de tolérance de décalage d’horloge par défaut.
Licence
Apache-2.0
Version vérifiée
10.10 ·
Code source
bitbucket.org/connect2id/nimbus-jose-jwt
Documentation
connect2id.com/products/nimbus-jose-jwt

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSAEd25519

Nimbus couvre JWS, JWE, JWK et JWT sur deux couches : des objets de bas niveau (SignedJWT, ECDSASigner, MACVerifier) et le DefaultJWTProcessor, qui réunit la sélection des clés, la vérification de la signature et le contrôle des claims. Le serveur de ressources de Spring Security repose sur lui, si bien que ses valeurs par défaut sont celles qu’appliquent la plupart des API Spring.

Avec le processeur, le JWSVerificationKeySelector constitue la liste d’autorisation : configuré pour ES256, il a refusé un jeton RS256 ainsi qu’un jeton HS256 dont la clé était le texte PEM de la clé EC, avec Another algorithm expected, or no matching key(s) found, et sans aucun sélecteur, il refuse tout. Les jetons non signés sont rejetés (Unsecured (plain) JWTs are rejected, extend class to handle). L’API de bas niveau n’offre pas cette protection (un MACVerifier construit à partir du texte PEM a vérifié ce jeton HS256 forgé) : le code qui choisit un vérificateur d’après l’en-tête du jeton recrée donc le bug classique de confusion d’algorithme. Utilisez le processeur, comme le fait l’exemple.

DefaultJWTClaimsVerifier tolère par défaut 60 secondes de décalage d’horloge, et exp est facultatif sauf s’il figure parmi les claims obligatoires (l’exemple exige sub et exp et fixe l’émetteur). ES256 refuse une clé P-384 (The ES256 algorithm is not allowed or supported by the JWS signer), et une signature encodée en DER échoue simplement à la vérification ; contrôlez la longueur de la signature avec le débogueur JWT.

La version 10.10 a fonctionné en aller-retour avec les familles HS, RS, PS et ES, ainsi qu’avec EdDSA et le nom plus récent Ed25519. Ed25519 nécessite Google Tink dans le classpath : sans lui, la signature a échoué avec NoClassDefFoundError: com/google/crypto/tink/subtle/Ed25519Sign$KeyPair. ES256K nécessite un fournisseur qui prend en charge secp256k1, comme Bouncy Castle, car le JDK n’en propose aucun.

Pour contrôler ce que publie un fournisseur d’identité, utilisez le vérificateur JWKS ; pour les jetons chiffrés, l’inspecteur JWE.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/nimbus-jose-jwt/run.sh a exécuté Nimbus JOSE + JWT 10.10 dans l’image de conteneur maven:3.9.16-eclipse-temurin-25 le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSAEd25519

    Signature et vérification réussies sur le JDK avec HS, RS, PS et ES en 256/384/512, EdDSA et Ed25519 ; ES256K et Ed448 n’ont pas fonctionné en aller-retour avec les fournisseurs du JDK.

    Relevé par la vérification
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA, Ed25519
    evidence jws-algorithms: ES256K: not round-tripped with the JDK providers
    evidence jws-algorithms: Ed448: not round-tripped with the JDK providers
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Un jeton avec une signature ES256 encodée en DER a échoué à la vérification.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: not verified
  • Liste d’algorithmes autorisés

    required

    DefaultJWTProcessor a besoin d’un sélecteur de clés : sans sélecteur, il échoue avec No JWS key selector is configured, et un sélecteur configuré pour ES256 a rejeté un jeton RS256.

    Relevé par la vérification
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: DefaultJWTProcessor with a JWSVerificationKeySelector for ES256, RS256 token: BadJOSEException: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
    evidence jwt-algorithm-allowlist: DefaultJWTProcessor without a key selector: BadJOSEException: Signed JWT rejected: No JWS key selector is configured
  • Jetons non signés (alg none)

    rejected

    Le processeur rejette les jetons non signés avec Unsecured (plain) JWTs are rejected, extend class to handle.

    Relevé par la vérification
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: DefaultJWTProcessor: BadJOSEException: Unsecured (plain) JWTs are rejected, extend class to handle
    evidence jwt-alg-none: SignedJWT.parse: ParseException: Invalid JWS header: Not a JWS header
  • Clé publique utilisée comme secret HMAC

    rejected

    Un jeton HS256 dont la clé est le texte PEM a été rejeté par le processeur configuré pour ES256, mais un MACVerifier construit manuellement à partir de ce texte PEM l’a vérifié.

    Relevé par la vérification
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: DefaultJWTProcessor with the ES256 key selector: BadJOSEException: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
    evidence jwt-hmac-public-key: a MACVerifier built from the PEM text by hand: verified
  • Courbe vérifiée par rapport à alg

    enforced

    Signer en ES256 avec une clé P-384 échoue avec The ES256 algorithm is not allowed or supported by the JWS signer: Supported algorithms: [ES384].

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: JOSEException: The ES256 algorithm is not allowed or supported by the JWS signer: Supported algorithms: [ES384]
  • Décalage d’horloge toléré par défaut

    60s

    DefaultJWTClaimsVerifier tolère 60 secondes de décalage (DEFAULT_MAX_CLOCK_SKEW_SECONDS).

    Relevé par la vérification
    fact jwt-clock-skew = 60s
    evidence jwt-clock-skew: DefaultJWTClaimsVerifier.DEFAULT_MAX_CLOCK_SKEW_SECONDS = 60
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a passé le vérificateur de claims par défaut ; déclarer exp comme obligatoire l’a fait échouer avec JWT missing required claims: [exp].

    Relevé par la vérification
    fact jwt-exp-required = no
    evidence jwt-exp-required: with DefaultJWTClaimsVerifier requiring exp: BadJWTException: JWT missing required claims: [exp]
  • Ed25519 sans Google Tink

    fails

    Sans Google Tink dans le classpath, la signature avec Ed25519Signer a échoué avec NoClassDefFoundError: com/google/crypto/tink/subtle/Ed25519Sign$KeyPair.

    Relevé par la vérification
    fact ed25519-without-tink = fails
    evidence ed25519-without-tink: NoClassDefFoundError: com/google/crypto/tink/subtle/Ed25519Sign$KeyPair

Utilisation

Signer un jeton ES256 avec un identifiant de clé, puis le traiter avec un sélecteur de clés limité à ES256 et un vérificateur de claims qui exige sub, exp et l’émetteur.
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.proc.*;
import com.nimbusds.jwt.*;
import com.nimbusds.jwt.proc.*;
import java.util.*;

public class Usage {
    public static void main(String[] args) throws Exception {
        ECKey key = new ECKeyGenerator(Curve.P_256).keyID("2026-10").generate();
        JWTClaimsSet claims = new JWTClaimsSet.Builder().subject("user-42").issuer("https://auth.example.com")
            .expirationTime(new Date(System.currentTimeMillis() + 600_000)).build();
        SignedJWT jwt = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(key.getKeyID()).build(), claims);
        jwt.sign(new ECDSASigner(key));

        // The key selector is the algorithm allow-list; the claims verifier requires iss and exp.
        DefaultJWTProcessor<SecurityContext> processor = new DefaultJWTProcessor<>();
        processor.setJWSKeySelector(new JWSVerificationKeySelector<>(JWSAlgorithm.ES256, new ImmutableJWKSet<>(new JWKSet(key.toPublicJWK()))));
        processor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(
            new JWTClaimsSet.Builder().issuer("https://auth.example.com").build(), new HashSet<>(List.of("sub", "exp"))));
        System.out.println("sub: " + processor.process(jwt.serialize(), null).getSubject());
    }
}

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur maven:3.9.16-eclipse-temurin-25, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
mvn -q -B dependency:copy -Dartifact=com.nimbusds:nimbus-jose-jwt:10.10 -DoutputDirectory=lib >/dev/null
Commande
java -cp lib/nimbus-jose-jwt-10.10.jar Usage.java
La sortie doit correspondre à cette expression régulière
sub: user-42

Bibliothèques