Node.js 24.22.0 LTS, surnommée “Krypton”, s’est imposée comme la version de référence pour les équipes qui déploient du code de production en octobre 2026. Cette version expose nativement Ed25519 via le module crypto, sans dépendance externe, ce qui change la façon dont les développeurs francophones abordent la signature de messages, de jetons ou de licences logicielles.
Le choix d’un algorithme de signature n’est plus seulement une question de sécurité. Il touche aussi à la taille des clés, à la vitesse de vérification et à la simplicité du code. Ed25519 répond à ces trois critères avec une clé publique de 32 octets et une signature de 64 octets, nettement plus compactes que leurs équivalentes RSA. Notre comparatif Ed25519 vs ECDSA P-256 détaillait déjà l’écart de performance entre les deux courbes, ce tutoriel passe maintenant à l’implémentation pratique.
Ce guide couvre treize étapes concrètes, de l’installation de Node.js à la construction d’un petit outil de signature de licences, en passant par les pièges classiques et un tableau de dépannage. Chaque bloc de code a été vérifié avec Node.js 24.22.0 et les dernières versions des bibliothèques Noble.
Selon l’enquête 2025 Stack Overflow Developer Survey, JavaScript reste utilisé par 66 % des développeurs interrogés, source qui confirme pourquoi Node.js demeure le terrain privilégié pour démocratiser des pratiques cryptographiques solides. Ed25519, conçu pour être simple à implémenter correctement, s’adresse justement à cette majorité de développeurs qui ne sont pas des cryptographes de métier.
La signature de données dépasse largement le cadre des messages applicatifs. Un éditeur de logiciel signe ses artefacts de build pour prouver leur provenance, une plateforme CI/CD signe un SBOM (Software Bill of Materials) pour en garantir l’intégrité, et un fournisseur d’API signe chaque webhook pour que le destinataire puisse rejeter une requête forgée. Ed25519 convient particulièrement bien à ces trois cas, car la vérification reste rapide même sur un poste client modeste, et la petite taille des signatures pèse peu dans un en-tête HTTP ou un fichier de manifeste.
EdDSA et Curve25519 : ce qu’il faut comprendre avant de coder
Ed25519 appartient à la famille EdDSA (Edwards-curve Digital Signature Algorithm), construite sur la courbe Edwards dérivée de Curve25519. Contrairement à ECDSA, qui a besoin d’un générateur de nombres aléatoires fiable à chaque signature, Ed25519 produit des signatures déterministes. La même clé privée et le même message donnent toujours la même signature. Cette propriété élimine une classe entière de failles liées à un générateur aléatoire défaillant, comme celles qui ont touché certaines implémentations ECDSA par le passé.
Le module crypto de Node.js traite Ed25519 comme un algorithme de signature à part entière, distinct de l’échange de clés. Pour l’échange de clés sur la même famille de courbes, Node.js utilise X25519, détaillé dans notre tutoriel sur l’échange de clés ECDH en Node.js. Ed25519 signe, X25519 échange des secrets, et il ne faut jamais mélanger les deux rôles dans une même ligne de code.
Deux détails techniques structurent tout le reste de ce tutoriel. D’abord, Ed25519 gère son propre hachage interne selon la spécification, donc la fonction sign() de Node.js attend null comme paramètre de digest. Ensuite, la clé publique Ed25519 mesure exactement 32 octets en format brut, mais ce nombre grossit une fois encodée en PEM ou en SPKI à cause de l’enveloppe ASN.1 qui l’entoure.
Déterminisme : l’avantage silencieux d’Ed25519
Le caractère déterministe d’Ed25519 paraît anodin au premier abord, mais il règle un problème récurrent dans l’histoire des courbes elliptiques. Avec ECDSA, une source d’aléa partiellement prévisible ou réutilisée entre deux signatures peut suffire à exposer la clé privée entière, un scénario qui s’est déjà produit sur des systèmes embarqués mal configurés. Ed25519 dérive son nombre aléatoire interne du hachage de la clé privée et du message lui-même, ce qui élimine ce point de défaillance sans demander au développeur de gérer une source d’entropie externe. En pratique, cela signifie qu’un test unitaire peut comparer deux signatures produites à des moments différents pour la même entrée et s’attendre à une égalité stricte, un comportement impossible à reproduire avec ECDSA.
Prérequis : versions et outils pour suivre ce tutoriel
Avant de lancer la première commande, voici l’environnement exact utilisé pour écrire et vérifier chaque exemple de ce tutoriel.
| Outil | Version | Rôle |
|---|---|---|
| Node.js | 24.22.0 LTS (Krypton) | Exécution du code et module crypto natif |
| npm | fourni avec Node.js 24.22.0 | Gestion des dépendances |
| @noble/curves | 2.4.0 | Implémentation alternative en JavaScript pur |
| @noble/ed25519 | 3.2.0 | Implémentation légère dédiée à Ed25519 |
| Système | Linux, macOS ou Windows avec WSL | Terminal pour exécuter les commandes |
| Éditeur | VS Code ou équivalent | Édition et exécution des scripts |
Les exemples utilisent des modules ES (import/export), donc le fichier package.json du projet doit déclarer "type": "module". Les commandes d’installation ciblent précisément les versions 2.4.0 et 3.2.0 pour éviter les changements d’API entre versions majeures de ces paquets. Le seuil de Node.js 20.19 n’est pas arbitraire : c’est la première branche à exposer Ed25519 et X25519 comme algorithmes stables dans l’API Web Crypto, et @noble/curves 2.x s’appuie sur cette même base pour garantir un comportement identique entre le module natif et l’implémentation pure JavaScript.
Étape 1 : installer Node.js 24.22.0 LTS
La version 24.22.0, surnommée Krypton, est la branche LTS (Long Term Support) active au moment de la rédaction. L’installer via un gestionnaire de versions comme nvm évite les conflits avec d’autres projets qui utilisent encore une version antérieure de Node.js.
nvm install 24.22.0
nvm use 24.22.0
node --version
La commande affiche v24.22.0 si l’installation a réussi. Toute version antérieure à 20.19 bloquera plus tard l’import de @noble/curves en v2.x, qui exige strictement ce seuil minimal.
Étape 2 : initialiser le projet et activer les modules ES
Un nouveau dossier isolé évite de mélanger ce projet avec d’autres dépendances déjà installées sur la machine.
mkdir signature-ed25519 && cd signature-ed25519
npm init -y
npm pkg set type="module"
La commande npm pkg set type="module" ajoute directement l’option dans package.json, ce qui permet d’utiliser import et export dans tous les fichiers .js du projet sans passer par l’extension .mjs.
Étape 3 : générer une paire de clés Ed25519 avec le module crypto natif
Node.js génère une paire de clés Ed25519 en une seule ligne grâce à generateKeyPairSync. Aucune bibliothèque externe n’est nécessaire à ce stade.
import { generateKeyPairSync } from 'node:crypto';
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
console.log(publicKey.export({ type: 'spki', format: 'pem' }).toString());
console.log(privateKey.export({ type: 'pkcs8', format: 'pem' }).toString());
Le résultat ressemble à ceci :
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA3f2hk9te+PgQ...
-----END PUBLIC KEY-----
Les objets publicKey et privateKey sont des instances de KeyObject. Elles ne révèlent les octets bruts de la clé qu’au moment de l’export, ce qui réduit le risque de fuite accidentelle dans les journaux applicatifs. Un appel à console.log(privateKey) sans export affiche d’ailleurs un objet opaque de type [KeyObject], et non la clé elle-même, ce qui protège contre un oubli de débogage qui finirait dans un fichier de log partagé.
Étape 4 : signer un message avec sign()
La fonction sign() accepte null comme premier argument pour Ed25519, car l’algorithme calcule lui-même son hachage interne.
import { sign } from 'node:crypto';
const message = Buffer.from('Message signé par Ed25519', 'utf8');
const signature = sign(null, message, privateKey);
console.log(signature.length);
console.log(signature.toString('base64'));
La sortie affiche 64 puis une chaîne Base64 d’environ 88 caractères. Chaque signature Ed25519 mesure 64 octets, quelle que soit la longueur du message signé. Un message de dix caractères et un message de dix mille caractères produisent une signature de taille identique.
Étape 5 : vérifier une signature avec verify()
import { verify } from 'node:crypto';
const estValide = verify(null, message, publicKey, signature);
console.log(estValide);
// true
verify() renvoie un booléen, jamais une exception en cas de signature invalide. Un oubli fréquent consiste à traiter l’absence d’exception comme une preuve de validité, alors qu’il faut explicitement tester la valeur renvoyée avant d’autoriser la suite du traitement.
Étape 6 : exporter et importer les clés au format PEM
Stocker les clés sous forme de fichiers PEM permet de les réutiliser entre plusieurs scripts ou de les déployer sur un serveur distinct de celui qui les a générées. Ce mécanisme d’export rejoint celui utilisé pour les certificats, abordé dans notre tutoriel sur la signature numérique X.509 en Node.js.
import { writeFileSync, readFileSync } from 'node:fs';
import { createPrivateKey, createPublicKey } from 'node:crypto';
writeFileSync('cle-privee.pem', privateKey.export({ type: 'pkcs8', format: 'pem' }));
writeFileSync('cle-publique.pem', publicKey.export({ type: 'spki', format: 'pem' }));
const clePriveeImportee = createPrivateKey(readFileSync('cle-privee.pem'));
const clePubliqueImportee = createPublicKey(readFileSync('cle-publique.pem'));
Le fichier cle-privee.pem doit rester hors du dépôt Git et hors de tout backup non chiffré. Un fichier .gitignore qui exclut *.pem évite une fuite accidentelle lors d’un premier commit.
Étape 7 : encoder la signature en Base64 pour le transport réseau
Une API REST ou un webhook transmet rarement des octets bruts. Encoder la signature, le message et la clé publique en JSON facilite leur transport entre deux services.
const charge = {
message: message.toString('utf8'),
signature: signature.toString('base64'),
clePublique: publicKey.export({ type: 'spki', format: 'pem' }).toString()
};
console.log(JSON.stringify(charge, null, 2));
Base64 standard inclut les caractères + et /, qui posent parfois problème dans une URL. L’alternative base64url, disponible via signature.toString('base64url'), évite ce souci sans étape de conversion manuelle supplémentaire.
Étape 8 : détecter une altération du message signé
Une signature protège l’intégrité du message. Modifier un seul caractère doit invalider la vérification, sans exception.
const messageAltere = Buffer.from('Message signe par Ed25519', 'utf8');
const toujoursValide = verify(null, messageAltere, publicKey, signature);
console.log(toujoursValide);
// false
Ce test simple sert de garde-fou dans une suite de tests automatisés. Si verify() renvoie true après une altération, le bug se situe presque toujours dans la façon dont le message est reconstruit avant l’appel, rarement dans l’algorithme lui-même.
Étape 9 : utiliser l’API Web Crypto pour du code portable
Le module node:crypto ne fonctionne qu’en environnement Node.js. L’API Web Crypto, disponible aussi bien dans Node.js que dans les navigateurs modernes, permet de partager le même code de vérification côté client et côté serveur.
const { publicKey: clePub, privateKey: clePriv } = await crypto.subtle.generateKey(
{ name: 'Ed25519' },
true,
['sign', 'verify']
);
const encode = new TextEncoder().encode('Message signé');
const signatureWC = await crypto.subtle.sign('Ed25519', clePriv, encode);
const valideWC = await crypto.subtle.verify('Ed25519', clePub, signatureWC, encode);
console.log(valideWC);
Le support stable d’Ed25519 et de X25519 dans l’API Web Crypto de Node.js remonte à la branche 20.19.3, selon la documentation officielle du projet Node.js. Les navigateurs basés sur Chromium et Firefox récents suivent la même spécification du W3C, ce qui garantit un comportement identique des deux côtés. Un projet qui vise aussi bien un backend Node.js qu’une extension de navigateur ou une application Electron gagne donc à écrire sa logique de vérification une seule fois autour de crypto.subtle, plutôt que de maintenir deux implémentations parallèles qui risquent de diverger avec le temps.
Étape 10 : implémentation alternative avec @noble/curves
@noble/curves propose une implémentation Ed25519 entièrement écrite en JavaScript, utile pour du code qui doit tourner hors de Node.js ou dans un bac à sable sans accès au module crypto natif. La version 2.4.0 du paquet nécessite Node.js 20.19 ou plus récent et fonctionne uniquement en ESM, d’après la documentation publiée sur JSR.
npm install @noble/[email protected]
import { ed25519 } from '@noble/curves/ed25519.js';
const clePriveeBrute = ed25519.utils.randomPrivateKey();
const clePubliqueBrute = ed25519.getPublicKey(clePriveeBrute);
const msg = new TextEncoder().encode('Bonjour, Europe');
const sig = ed25519.sign(msg, clePriveeBrute);
console.log(ed25519.verify(sig, msg, clePubliqueBrute));
// true
Le paquet historique noble-ed25519 (version 1.2.6) est marqué comme déprécié par son auteur et ne doit plus servir de base à un nouveau projet, comme l’indique sa fiche sur npm.io. @noble/ed25519 3.2.0, plus léger que @noble/curves, convient mieux si le projet n’a besoin que d’Ed25519 et d’aucune autre courbe elliptique.
Étape 11 : construire un projet complet, signer des licences logicielles
Ce petit outil en ligne de commande illustre un cas d’usage réel. Il génère une paire de clés, signe un fichier de licence pour un client, puis vérifie cette licence avant d’autoriser l’exécution d’un logiciel.
// licence.mjs
import { generateKeyPairSync, sign, verify } from 'node:crypto';
import { writeFileSync, readFileSync } from 'node:fs';
function genererCles() {
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
writeFileSync('cle-privee.pem', privateKey.export({ type: 'pkcs8', format: 'pem' }));
writeFileSync('cle-publique.pem', publicKey.export({ type: 'spki', format: 'pem' }));
console.log('Paire de clés générée.');
}
function signerLicence(client, expiration) {
const privateKey = readFileSync('cle-privee.pem');
const licence = JSON.stringify({ client, expiration });
const signature = sign(null, Buffer.from(licence), privateKey);
const fichier = { licence, signature: signature.toString('base64') };
writeFileSync('licence.json', JSON.stringify(fichier, null, 2));
console.log('Licence signée pour', client);
}
function verifierLicence() {
const publicKey = readFileSync('cle-publique.pem');
const { licence, signature } = JSON.parse(readFileSync('licence.json', 'utf8'));
const valide = verify(null, Buffer.from(licence), publicKey, Buffer.from(signature, 'base64'));
console.log(valide ? 'Licence valide' : 'Licence invalide ou falsifiée');
}
const commande = process.argv[2];
if (commande === 'generer') genererCles();
else if (commande === 'signer') signerLicence(process.argv[3], process.argv[4]);
else if (commande === 'verifier') verifierLicence();
else console.log('Usage: node licence.mjs [generer|signer|verifier]');
node licence.mjs generer
node licence.mjs signer "Acme SARL" "2027-10-17"
node licence.mjs verifier
La sortie affiche successivement :
Paire de clés générée.
Licence signée pour Acme SARL
Licence valide
En production, la clé privée cle-privee.pem reste uniquement sur le serveur de l’éditeur du logiciel. Seule la clé publique cle-publique.pem part dans chaque copie distribuée du logiciel, ce qui permet à n’importe quelle installation de vérifier la licence sans jamais pouvoir en forger une nouvelle.
Pourquoi séparer génération, signature et vérification en trois commandes
Le script sépare volontairement trois responsabilités dans trois fonctions distinctes plutôt que d’enchaîner les opérations dans un seul bloc de code. En conditions réelles, la génération de clés s’exécute une seule fois sur le poste de l’éditeur, la signature tourne à chaque vente sur un serveur qui détient la clé privée, et la vérification s’exécute potentiellement des milliers de fois sur des machines qui n’ont accès qu’à la clé publique. Garder ces trois étapes séparées dans le code facilite aussi les tests : on peut vérifier qu’une licence signée avec une ancienne clé privée échoue proprement une fois cette clé révoquée, sans toucher au reste de la logique applicative.
Étape 12 : valider l’implémentation avec des tests croisés et des tests négatifs
Les vecteurs de test officiels d’Ed25519 sont publiés dans la RFC 8032 de l’IETF. Plutôt que de recopier des chaînes hexadécimales sujettes à une erreur de transcription, ce test croise deux implémentations indépendantes et vérifie qu’elles produisent une signature identique à partir de la même clé.
import { generateKeyPairSync, sign, verify } from 'node:crypto';
import { ed25519 } from '@noble/curves/ed25519.js';
import assert from 'node:assert';
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const message = Buffer.from('Vecteur de test croisé', 'utf8');
const sigNode = sign(null, message, privateKey);
const jwk = privateKey.export({ format: 'jwk' });
const rawPriv = Buffer.from(jwk.d, 'base64url');
const sigNoble = ed25519.sign(message, rawPriv);
assert.strictEqual(Buffer.from(sigNode).toString('hex'), Buffer.from(sigNoble).toString('hex'));
assert.ok(verify(null, message, publicKey, sigNode));
assert.ok(!verify(null, Buffer.from('Message modifié'), publicKey, sigNode));
console.log('Les deux implémentations produisent une signature identique.');
console.log('Test négatif (message modifié) rejeté comme attendu.');
Cette approche détecte une régression introduite par une mise à jour de dépendance plus rapidement qu’un simple test unitaire isolé, parce qu’elle compare deux chemins de code indépendants plutôt qu’un seul.
Étape 13 : stocker les clés, planifier leur rotation et auditer les dépendances
Une clé privée Ed25519 ne doit jamais apparaître en clair dans une variable d’environnement partagée, un fichier de configuration versionné ou un message de log. Un coffre-fort comme HashiCorp Vault, un service de gestion de clés cloud ou un HSM applique un chiffrement au repos et consigne chaque accès.
La rotation des clés suit un calendrier fixé par la politique de sécurité de l’organisation, par exemple tous les douze mois ou immédiatement après le départ d’un collaborateur qui y avait accès. Chaque rotation exige de republier la nouvelle clé publique auprès de toutes les parties qui vérifient des signatures.
npm audit
npm outdated
npm ls @noble/curves @noble/ed25519
npm audit affiche found 0 vulnerabilities si les dépendances sont à jour, ou détaille chaque paquet vulnérable avec son niveau de gravité. Traiter toute clé publique reçue d’un tiers comme une donnée non fiable jusqu’à vérification de sa provenance évite une classe d’attaques où un attaquant substitue sa propre clé publique à celle de la victime.
Ed25519 face à ECDSA et RSA : le comparatif chiffré
Les trois algorithmes résolvent le même problème (prouver qu’un message vient bien du détenteur d’une clé privée) mais avec des compromis très différents sur la taille des données et les exigences d’implémentation.
| Algorithme | Taille clé publique | Taille signature | Aléa requis par signature |
|---|---|---|---|
| Ed25519 | 32 octets | 64 octets | Aucun (déterministe) |
| ECDSA P-256 | 64 octets (non compressée) | ~70 à 72 octets (DER) | Oui, à chaque signature |
| RSA-2048 | 256 octets | 256 octets | Oui (padding) |
Notre tutoriel sur la signature ECDSA en Node.js détaille l’implémentation de la colonne du milieu avec le même niveau de précision que ce guide pour Ed25519. Le choix entre les deux dépend surtout de l’écosystème cible : un projet qui doit interopérer avec une infrastructure ECDSA existante n’a pas de raison de migrer uniquement pour gagner quelques octets.
Combiner Ed25519 avec un chiffrement symétrique pour des messages confidentiels
Ed25519 garantit qu’un message vient bien de l’expéditeur attendu et n’a pas été modifié en chemin, mais il ne protège pas sa confidentialité. Un message signé reste parfaitement lisible par quiconque l’intercepte. Pour couvrir les deux besoins à la fois, le schéma le plus courant signe d’abord le message en clair, puis chiffre l’ensemble (message et signature) avec un algorithme symétrique comme AES-256-GCM, en s’appuyant sur une clé de session dérivée d’un échange X25519 séparé.
import { sign } from 'node:crypto';
import { createCipheriv, randomBytes } from 'node:crypto';
const message = Buffer.from('Rapport confidentiel', 'utf8');
const signature = sign(null, message, privateKey);
const cle = randomBytes(32);
const iv = randomBytes(12);
const chiffreur = createCipheriv('aes-256-gcm', cle, iv);
const chiffre = Buffer.concat([
chiffreur.update(Buffer.concat([message, signature])),
chiffreur.final()
]);
const tag = chiffreur.getAuthTag();
Le destinataire déchiffre d’abord avec AES-256-GCM, vérifie le tag d’authentification, puis extrait le message et la signature pour lancer la vérification Ed25519 proprement dite. L’ordre des opérations compte : signer avant de chiffrer protège contre un attaquant qui tenterait de faire passer un texte chiffré arbitraire pour un message légitime, alors que chiffrer avant de signer exposerait la signature à des attaques de malléabilité propres à certains modes de chiffrement.
Pièges courants à éviter avec Ed25519 en Node.js
- Oublier de passer null comme digest :
sign(null, ...)etverify(null, ...)sont obligatoires pour Ed25519, car l’algorithme hache le message lui-même. - Confondre clé brute et clé encodée : une clé publique brute Ed25519 fait 32 octets, mais son export PEM ou SPKI en fait beaucoup plus à cause de l’enveloppe ASN.1.
- Utiliser Ed25519 pour chiffrer : Ed25519 signe, il ne chiffre jamais rien. Pour chiffrer, il faut combiner X25519 avec un algorithme symétrique comme AES-256-GCM.
- Installer le paquet déprécié noble-ed25519 : ce paquet historique n’est plus maintenu, mieux vaut préférer @noble/ed25519 3.2.0 ou @noble/curves 2.4.0.
- Mélanger Ed25519 et X25519 : signer avec une clé prévue pour l’échange de clés, ou inversement, provoque des erreurs silencieuses dans certaines bibliothèques.
- Ignorer le verrouillage de version : @noble/curves 2.x est ESM uniquement et exige Node.js 20.19 ou plus récent, une mise à jour non verrouillée casse la compatibilité.
- Traiter verify() comme une exception : la fonction renvoie un booléen, jamais une erreur, donc un code qui ignore la valeur de retour laisse passer des signatures invalides.
Dépannage : 9 problèmes fréquents et leurs solutions
| Problème | Cause probable | Solution |
|---|---|---|
| “ERR_OSSL_EVP_UNSUPPORTED” pendant generateKeyPairSync | Version de Node.js ou d’OpenSSL trop ancienne | Mettre à jour vers Node.js 24.22.0 LTS |
| verify() renvoie toujours false | Message reconstruit avec un encodage différent de celui utilisé à la signature | Vérifier que l’encodage (utf8, hex, base64) est identique des deux côtés |
| “Cannot use import statement outside a module” | package.json sans la propriété type module | Exécuter npm pkg set type=”module” |
| “Cannot find module ‘@noble/curves/ed25519′” | Import sans l’extension .js requise en v2.x | Importer depuis ‘@noble/curves/ed25519.js’ |
| Erreur de lecture PEM au moment de l’import | Fichier PEM tronqué ou type pkcs8/spki inversé | Vérifier le type d’export (pkcs8 pour la clé privée, spki pour la publique) |
| npm audit signale noble-ed25519 comme vulnérable | Dépendance transitive dépréciée dans l’arbre npm | Migrer vers @noble/ed25519 3.2.0 ou @noble/curves 2.4.0 |
| crypto.subtle.generateKey échoue avec “Ed25519” | Navigateur ou runtime trop ancien pour le Web Crypto Ed25519 | Utiliser node:crypto côté serveur ou mettre à jour le navigateur |
| Signature valide en local mais invalide après transport réseau | Corruption du Base64 (retours à la ligne ou caractères d’URL) | Utiliser base64url ou vérifier l’intégrité du transport JSON |
| “TypeError: key.export is not a function” | Clé manipulée comme un buffer brut plutôt qu’un KeyObject | Reconstruire l’objet avec createPrivateKey ou createPublicKey avant d’exporter |
Astuces avancées pour aller plus loin avec Ed25519
Les clés SSH modernes utilisent très largement Ed25519 depuis qu’OpenSSH l’a adopté comme type de clé couramment recommandé. La commande ssh-keygen -t ed25519 remplace désormais ssh-keygen -t rsa dans la plupart des guides de configuration récents.
Les jetons JWT supportent l’algorithme EdDSA via le paramètre "alg": "EdDSA" dans leur en-tête. Notre tutoriel sur les jetons JWT RS256/ES256 en Node.js détaille la mécanique générale de signature de jetons, transposable à Ed25519 avec les mêmes principes de vérification côté serveur.
La norme WebAuthn et le protocole FIDO2, à la base des clés de sécurité physiques et des passkeys, reconnaissent Ed25519 comme l’un des algorithmes de signature COSE disponibles pour l’authentification sans mot de passe.
Pour les volumes élevés de vérifications, par exemple une passerelle API qui valide des milliers de requêtes signées par seconde, regrouper les vérifications par lot réduit la charge CPU par rapport à des appels individuels répétés. Les bibliothèques Noble exposent des fonctions dédiées à ce type de traitement.
Les modules matériels de sécurité (HSM) et les services de gestion de clés cloud ajoutent progressivement la prise en charge d’Ed25519 à leurs catalogues d’algorithmes, aux côtés de RSA et d’ECDSA déjà établis depuis longtemps. Faire signer par un HSM plutôt que par une clé stockée sur disque retire la clé privée de la portée d’un attaquant qui compromettrait uniquement le serveur applicatif, puisque l’opération de signature s’exécute à l’intérieur du module matériel sans jamais exposer les octets de la clé en dehors de son enceinte.
Ed25519 ne résiste pas à un futur ordinateur quantique suffisamment puissant, au même titre qu’ECDSA et RSA. Les équipes qui anticipent cette transition combinent déjà une signature classique avec une signature post-quantique comme ML-DSA dans un schéma hybride, plutôt que de migrer d’un seul bloc.
Adoption réelle d’Ed25519 : SSH, TLS et cryptomonnaies
Dans le protocole SSH, Ed25519 s’est imposé comme un choix par défaut pour les nouvelles paires de clés, grâce à des clés et des signatures nettement plus compactes que RSA ou ECDSA. ECDSA avec la courbe nistp256 reste pris en charge, mais de nombreux guides d’administration système recommandent désormais Ed25519 en premier lieu.
Côté TLS, la situation est plus nuancée. La norme TLS 1.3 reconnaît Ed25519 comme schéma de signature valide, mais l’interopérabilité avec l’écosystème des autorités de certification publiques a historiquement favorisé ECDSA et RSA. Ed25519 domine surtout dans les environnements où l’éditeur contrôle les deux extrémités de la connexion, comme les API internes ou les infrastructures logicielles maison.
Le projet open source HAP-NodeJS, par exemple, a migré vers le module crypto natif de Node.js pour ses opérations Curve25519 et Ed25519 plutôt que de maintenir sa propre implémentation, un signe du mouvement général vers les primitives natives plutôt que des bibliothèques tierces.
Dans l’univers des cryptomonnaies, Ed25519 ou des variantes proches servent de base à la signature des transactions sur des écosystèmes comme Solana ou certains outils liés à Cardano. Bitcoin et Ethereum restent construits sur ECDSA avec la courbe secp256k1, ce qui illustre bien qu’aucun algorithme n’a remplacé l’autre à l’échelle globale. Chaque écosystème a fait un choix à son lancement, coûteux à remettre en cause après coup.
Ce paysage fragmenté explique aussi pourquoi un développeur Node.js croise rarement un seul algorithme de signature dans sa carrière. L’enquête Stack Overflow 2025 rappelle que TypeScript, utilisé par 43,6 % des développeurs interrogés, s’est largement imposé au-dessus du JavaScript pur dans les nouveaux projets, ce qui pousse de plus en plus de bibliothèques de signature à publier des typages stricts pour Ed25519, ECDSA et RSA côte à côte plutôt qu’un seul algorithme par défaut.
Pour replacer ces choix dans une perspective plus large, notre dossier Cryptographie rassemble les tutoriels et analyses consacrés aux fonctions de hachage, aux signatures et à la montée des standards post-quantiques.
Questions fréquentes sur la signature Ed25519 en Node.js
Quelle est la différence entre Ed25519 et ECDSA ?
Les deux sont des algorithmes de signature sur courbe elliptique, mais Ed25519 produit des signatures déterministes sans générateur aléatoire externe, alors qu’ECDSA a besoin d’un nombre aléatoire fiable à chaque signature. Ed25519 utilise aussi des clés et des signatures plus compactes.
Ed25519 permet-il de chiffrer des messages ?
Non. Ed25519 signe des données pour garantir leur authenticité et leur intégrité, il ne chiffre rien. Pour chiffrer, il faut combiner X25519 (échange de clés) avec un algorithme symétrique comme AES-256-GCM.
Pourquoi la même signature revient-elle pour le même message ?
Ed25519 est déterministe par construction. Signer deux fois le même message avec la même clé privée produit exactement la même signature de 64 octets, ce qui est voulu et documenté dans la spécification de l’algorithme.
Faut-il préférer node:crypto ou @noble/curves ?
node:crypto convient à la majorité des projets Node.js car il ne nécessite aucune dépendance externe. @noble/curves ou @noble/ed25519 deviennent utiles pour du code portable vers le navigateur ou pour manipuler des clés brutes sans passer par des objets KeyObject.
Les autorités de certification TLS publiques acceptent-elles Ed25519 ?
TLS 1.3 reconnaît Ed25519 comme schéma de signature, mais le support varie selon les autorités de certification et les navigateurs. RSA et ECDSA restent plus répandus dans la PKI web publique en 2026.
Comment migrer une clé SSH RSA vers Ed25519 ?
La commande ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 génère une nouvelle paire de clés. Il faut ensuite ajouter la nouvelle clé publique à chaque serveur distant avant de retirer l’ancienne clé RSA.
Ed25519 résiste-t-il à un ordinateur quantique ?
Non. Ed25519 repose sur le problème du logarithme discret sur courbe elliptique, vulnérable à l’algorithme de Shor. Les organisations qui anticipent cette menace combinent Ed25519 avec un algorithme post-quantique comme ML-DSA dans un schéma hybride.
Où trouver les vecteurs de test officiels d’Ed25519 ?
La RFC 8032 de l’IETF publie les vecteurs de test de référence pour EdDSA et Ed25519. Les bibliothèques comme @noble/curves incluent aussi leurs propres suites de tests alignées sur cette RFC.
