ES256 vs EdDSA pour les JWT est une question plus étroite qu’ECDSA vs EdDSA en général. Les jetons ont la même taille, les deux sont rapides, et les deux sont recommandés plutôt que RS256 pour les nouveaux émetteurs. Ce qui diffère, c’est la prise en charge : ES256 est exigé ou attendu par de nombreuses plateformes et de nombreux magasins de clés, tandis qu’Ed25519 est plus simple à implémenter correctement et s’appelle désormais Ed25519 plutôt qu’EdDSA dans JOSE.
Le guide ES256 vs RS256 traite le versant RSA de la même décision ; cette page compare les deux options sur courbes elliptiques.
Vue d’ensemble
| ES256 | EdDSA / Ed25519 | |
|---|---|---|
alg JOSE | ES256 (RFC 7518 §3.4) | Ed25519 (RFC 9864) ; l’ancien EdDSA (RFC 8037), désormais déprécié |
| Courbe et fonction de hachage | P-256 avec SHA-256 | Edwards25519 avec SHA-512 |
| JWK | kty: EC, crv: P-256, x, y | kty: OKP, crv: Ed25519, x |
| JWK publique (mesurée, JSON compact) | 126 caractères | 79 caractères |
| Signature dans le jeton | 64 octets bruts r‖s, 86 caractères | 64 octets, 86 caractères |
| Jeton d’exemple (mesuré ci-dessous) | 232 caractères | 234 caractères (valeur alg plus longue) |
| Déterministe | Non (nonce aléatoire dans la plupart des bibliothèques) | Oui |
| Niveau d’exigence JWA | Recommandé+ | Facultatif |
| Identifiant COSE | −7 (ou −9 ESP256 dans la RFC 9864) | −19 (anciennement −8) |
| KMS cloud | AWS, Google, Azure : oui | AWS (depuis novembre 2025) et Google : oui ; Azure : non |
| WebCrypto des navigateurs | Tous les navigateurs | Firefox 129, Safari 17, Chrome 137 et versions ultérieures |
Taille et vitesse
Les deux signatures font 64 octets : les jetons ont donc la même longueur, à l’en-tête près. Le script ci-dessous, exécuté avec Node.js 26.10, signe les mêmes claims des deux façons avec node:crypto seul (du vrai code devrait utiliser une bibliothèque JOSE).
import { generateKeyPairSync, sign } from "node:crypto";
const b64url = (value) => Buffer.from(value).toString("base64url");
const payload = b64url(JSON.stringify({ iss: "https://auth.example.com", sub: "user-42", aud: "api", exp: 1792500000 }));
const es = generateKeyPairSync("ec", { namedCurve: "P-256" });
const esInput = `${b64url(JSON.stringify({ alg: "ES256", kid: "es-1" }))}.${payload}`;
const es256 = `${esInput}.${b64url(sign("sha256", Buffer.from(esInput), { key: es.privateKey, dsaEncoding: "ieee-p1363" }))}`;
const ed = generateKeyPairSync("ed25519");
const edInput = `${b64url(JSON.stringify({ alg: "Ed25519", kid: "ed-1" }))}.${payload}`;
const ed25519 = `${edInput}.${b64url(sign(null, Buffer.from(edInput), ed.privateKey))}`;
console.log("ES256 token", es256.length, "chars, public JWK", JSON.stringify(es.publicKey.export({ format: "jwk" })).length, "chars");
console.log("Ed25519 token", ed25519.length, "chars, public JWK", JSON.stringify(ed.publicKey.export({ format: "jwk" })).length, "chars");
ES256 token 232 chars, public JWK 126 chars
Ed25519 token 234 chars, public JWK 79 chars
La vitesse n’est pas un critère décisif : sur un cœur d’Apple M4 Max, OpenSSL 3.6.5 produit environ 87 000 signatures P-256 et 60 000 signatures Ed25519 par seconde. Un JWKS contenant des clés Ed25519 est plus petit, car les clés OKP n’ont qu’une coordonnée.
Encodage et exactitude
Dans un JWS, les signatures ES256 doivent avoir la forme brute r‖s de 64 octets (RFC 7518 §3.4). De nombreuses API ECDSA produisent du DER par défaut : OpenSSL, SHA256withECDSA en Java, ecdsa.SignASN1 en Go, crypto.sign() de Node sans dsaEncoding, ainsi que les API de signature d’AWS KMS et de Google Cloud KMS. Du code ES256 écrit à la main qui oublie la conversion produit des signatures de 70 à 72 octets que les vérificateurs conformes rejettent, et l’erreur inverse fait échouer la vérification de jetons corrects. Les vérificateurs ECDSA doivent aussi rejeter un r ou un s égal à zéro ; Java 15 à 18 ne le faisait pas (CVE-2022-21449), ce qui permettait à n’importe qui de forger des jetons ES256 tant que le correctif n’était pas appliqué.
Ed25519 a un seul encodage de 64 octets, une signature déterministe et aucun nonce à mal gérer. Ses pièges sont ailleurs : certaines bibliothèques traitent la clé privée comme une graine de 32 octets, d’autres comme 64 octets, et les anciennes bibliothèques JOSE ne reconnaissent que "alg": "EdDSA".
Le nom alg a changé
La RFC 8037 (2017) a enregistré EdDSA comme algorithme JOSE polymorphe, dont la courbe est déterminée par la clé (crv: Ed25519 ou Ed448). La RFC 9864 (octobre 2025) déprécie les identifiants polymorphes et enregistre Ed25519 et Ed448 : l’alg seul détermine ainsi l’opération, comme l’a toujours fait ES256. La prise en charge reste inégale en 2026 : jose 6 accepte à la fois EdDSA et Ed25519, joserfc et Nimbus JOSE+JWT prennent en charge les nouveaux noms, tandis que PyJWT 2.15 et golang-jwt v5 n’acceptent toujours qu’EdDSA. Pour des jetons consommés par de nombreuses parties, vérifiez chaque consommateur avant de changer la valeur de l’en-tête ; pendant une transition, les vérificateurs peuvent accepter les deux noms pour la même clé.
Prise en charge par les plateformes et les magasins de clés
ES256 est le plus petit dénominateur commun. C’est le choix habituel pour les preuves DPoP (RFC 9449) et l’un des trois algorithmes qu’autorise FAPI 2.0 (PS256, ES256, ou EdDSA avec Ed25519) ; il est exigé pour l’App Store Server API d’Apple et les jetons de fournisseur APNs, pris en charge par tous les fournisseurs OpenID Connect, et c’est l’algorithme COSE par défaut de WebAuthn. Tous les HSM, TPM, Secure Enclave et KMS cloud proposent des clés P-256.
Côté logiciel, Ed25519 a rattrapé son retard : Node.js, Go, Java 15 et versions ultérieures, .NET via des bibliothèques, cryptography en Python, WebCrypto dans les navigateurs et les principales bibliothèques JOSE. Dans les magasins de clés, sa prise en charge est plus récente : AWS KMS a ajouté des clés de signature Ed25519 en novembre 2025, Google Cloud KMS propose EC_SIGN_ED25519, tandis que certains modèles de HSM et Azure Key Vault ne le proposent pas. Si les jetons doivent être signés dans un magasin de clés géré, consultez d’abord sa liste d’algorithmes.
Quand choisir l’un ou l’autre
- Fournisseur d’identité public, nombreuses parties de confiance (relying parties) : ES256 (en conservant RS256 pour les anciens consommateurs si nécessaire).
- Clés dans des HSM ou des KMS répartis sur plusieurs clouds : ES256, pour une prise en charge uniforme.
- Services internes, deux extrémités sous votre contrôle, bibliothèques modernes : Ed25519 avec
alg: Ed25519si tous les vérificateurs le prennent en charge, sinonEdDSA. - Jetons liés à l’expéditeur (DPoP) émis depuis des navigateurs et des applications mobiles : ES256, que tout magasin de clés de plateforme peut générer de façon non exportable.
- Profils qui imposent des algorithmes (FAPI, secteur public, finance) : ce que le profil indique.
Notes de migration
- Ajoutez la nouvelle clé au JWKS avec son propre
kidet unalgexplicite, et laissez aux consommateurs le temps de rafraîchir leurs caches. - Configurez les vérificateurs clé par clé : chaque clé accepte exactement un algorithme (et, pour Ed25519, les deux valeurs d’en-tête
EdDSAetEd25519si vos consommateurs diffèrent). N’acceptez jamais l’algdu jeton sans cette association. - Basculez l’émetteur sur la nouvelle clé ; les jetons existants continuent de se vérifier avec l’ancienne jusqu’à leur expiration.
- Retirez l’ancienne clé une fois écoulée la plus longue durée de vie des jetons.
- Testez les encodages de bout en bout : une signature ES256 produite par un KMS doit être convertie du DER au format brut avant d’entrer dans un jeton ; le convertisseur DER ⇄ brut et le débogueur JWT montrent ce que contient un jeton.
Références
- RFC 7518 §3.4, ES256 dans JWS
- RFC 8037, courbes CFRG et EdDSA dans JOSE
- RFC 9864, algorithmes entièrement spécifiés pour JOSE et COSE
- RFC 8725, bonnes pratiques actuelles pour les JWT
- Référence des spécifications de clés d’AWS KMS (ECC_NIST_EDWARDS25519)
- Google Cloud KMS : finalités et algorithmes des clés
- Profil de sécurité FAPI 2.0, §5.4.1, cryptographie
- CVE-2022-21449 (vérification des signatures ECDSA en Java)