java-jwt construit les jetons avec JWT.create()…sign(algorithm) et les vérifie avec JWT.require(algorithm)…build().verify(token). L’objet Algorithm porte à la fois l’algorithme et la clé, si bien qu’un vérificateur n’accepte jamais que cet unique algorithme : un jeton RS256, un jeton non signé et un jeton HS256 falsifié avec le texte PEM de la clé EC ont tous échoué face à un vérificateur ES256 avec AlgorithmMismatchException.
Cette protection ne tient que tant que l’Algorithm est choisi d’après la configuration. JWT.require(Algorithm.none()) accepte les jetons non signés, et Algorithm.HMAC256(pemBytes) a vérifié le jeton HS256 falsifié : un code qui choisit l’Algorithm d’après l’en-tête alg du jeton, ou qui charge les clés sous forme d’octets bruts, recrée les bugs de confusion d’algorithme que l’API empêche autrement. JWT.decode() analyse le jeton sans vérifier la signature ni aucun claim ; il a renvoyé un jeton expiré depuis une heure.
Le jeu d’algorithmes se compose de HMAC256/384/512, RSA256/384/512 et ECDSA256/384/512, sans PS256, ni ES256K, ni EdDSA : les jetons des émetteurs passés à RSA-PSS ou à Ed25519 exigent donc Nimbus ou JJWT. ES256 refuse une clé P-384 à la signature.
Les signatures ES256 sont au format r‖s brut de 64 octets, et une signature DER échoue avec le message générique The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA ; le débogueur JWT affiche la longueur de la signature. Il n’y a pas de marge de tolérance par défaut et exp est facultatif : l’exemple ajoute withClaimPresence("exp") et acceptLeeway(30) en plus du contrôle de l’émetteur.