ES256 vs. EdDSA für JWTs ist eine engere Frage als ECDSA vs. EdDSA im Allgemeinen. Die Tokens sind gleich groß, beide Verfahren sind schnell, und beide werden neuen Ausstellern statt RS256 empfohlen. Unterschiedlich ist die Unterstützung: Viele Plattformen und Schlüsselspeicher verlangen oder erwarten ES256, während sich Ed25519 einfacher korrekt implementieren lässt und in JOSE jetzt Ed25519 statt EdDSA heißt.
Die Anleitung zu ES256 vs. RS256 behandelt die RSA-Seite derselben Entscheidung; diese Seite vergleicht die beiden Optionen auf Basis elliptischer Kurven.
Auf einen Blick
| ES256 | EdDSA / Ed25519 | |
|---|---|---|
alg in JOSE | ES256 (RFC 7518 §3.4) | Ed25519 (RFC 9864); älter: EdDSA (RFC 8037), inzwischen veraltet |
| Kurve und Hash | P-256 mit SHA-256 | Edwards25519 mit SHA-512 |
| JWK | kty: EC, crv: P-256, x, y | kty: OKP, crv: Ed25519, x |
| Öffentlicher JWK (gemessen, kompaktes JSON) | 126 Zeichen | 79 Zeichen |
| Signatur im Token | 64 Byte r‖s im Raw-Format, 86 Zeichen | 64 Byte, 86 Zeichen |
| Beispiel-Token (unten gemessen) | 232 Zeichen | 234 Zeichen (längerer Wert für alg) |
| Deterministisch | Nein (zufällige Nonce in den meisten Bibliotheken) | Ja |
| Anforderungsstufe in JWA | Recommended+ | Optional |
| COSE-Kennung | −7 (oder −9 ESP256 nach RFC 9864) | −19 (älter: −8) |
| Cloud-KMS | AWS, Google, Azure: ja | AWS (seit November 2025) und Google: ja; Azure: nein |
| WebCrypto im Browser | Alle Browser | Ab Firefox 129, Safari 17, Chrome 137 |
Größe und Geschwindigkeit
Beide Signaturen sind 64 Byte groß, die Tokens sind also bis auf den Header gleich lang. Das folgende Skript, ausgeführt mit Node.js 26.10, signiert dieselben Claims auf beide Arten und nutzt dabei nur node:crypto (echter Code sollte eine JOSE-Bibliothek verwenden).
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
Die Geschwindigkeit gibt nicht den Ausschlag: Auf einem Kern eines Apple M4 Max erzeugt OpenSSL 3.6.5 etwa 87.000 P-256- und 60.000 Ed25519-Signaturen pro Sekunde. Ein JWKS mit Ed25519-Schlüsseln ist kleiner, weil OKP-Schlüssel nur eine Koordinate haben.
Kodierung und Korrektheit
ES256-Signaturen in JWS müssen im 64-Byte-Raw-Format r‖s vorliegen (RFC 7518 §3.4). Viele ECDSA-APIs liefern standardmäßig DER: OpenSSL, SHA256withECDSA in Java, ecdsa.SignASN1 in Go, crypto.sign() in Node ohne dsaEncoding sowie die Signatur-APIs von AWS KMS und Google Cloud KMS. Selbst geschriebener ES256-Code, der die Umwandlung vergisst, erzeugt Signaturen mit 70–72 Byte, die standardkonforme Verifizierer ablehnen; der umgekehrte Fehler lässt die Prüfung korrekter Tokens scheitern. ECDSA-Verifizierer müssen außerdem r oder s gleich null ablehnen; Java 15 bis 18 tat das nicht (CVE-2022-21449), sodass bis zum Patch jeder ES256-Tokens fälschen konnte.
Ed25519 hat nur eine 64-Byte-Kodierung, signiert deterministisch und hat keine Nonce, bei der etwas schiefgehen kann. Seine Fallen liegen anderswo: Manche Bibliotheken behandeln den privaten Schlüssel als 32-Byte-Seed, andere als 64 Byte, und ältere JOSE-Bibliotheken erkennen nur "alg": "EdDSA".
Die Kennung in alg hat sich geändert
RFC 8037 (2017) registrierte EdDSA als polymorphen JOSE-Algorithmus, dessen Kurve der Schlüssel bestimmt (crv: Ed25519 oder Ed448). RFC 9864 (Oktober 2025) erklärt polymorphe Kennungen für veraltet und registriert Ed25519 und Ed448, sodass alg allein die Operation festlegt, wie es bei ES256 schon immer der Fall war. Die Unterstützung ist 2026 uneinheitlich: jose 6 akzeptiert sowohl EdDSA als auch Ed25519, joserfc und Nimbus JOSE+JWT unterstützen die neuen Namen, während PyJWT 2.15 und golang-jwt v5 weiterhin nur EdDSA akzeptieren. Bei Tokens, die viele Parteien verarbeiten, prüfe jeden Empfänger, bevor du den Wert im Header umstellst; Verifizierer können während einer Übergangszeit beide Namen für denselben Schlüssel akzeptieren.
Unterstützung in Plattformen und Schlüsselspeichern
ES256 ist der kleinste gemeinsame Nenner. Für DPoP-Proofs (RFC 9449) ist es die übliche Wahl, und es ist einer von drei Algorithmen, die FAPI 2.0 zulässt (PS256, ES256 oder EdDSA mit Ed25519). Die App Store Server API von Apple und Provider-Tokens für APNs verlangen es, jeder Anbieter für OpenID Connect unterstützt es, und in WebAuthn ist es der standardmäßige COSE-Algorithmus. Jedes HSM, jedes TPM, jede Secure Enclave und jedes Cloud-KMS bietet P-256-Schlüssel an.
In Software hat Ed25519 aufgeholt: Node.js, Go, Java ab Version 15, .NET über Bibliotheken, cryptography für Python, WebCrypto im Browser und die wichtigsten JOSE-Bibliotheken. In Schlüsselspeichern ist es neuer: AWS KMS hat im November 2025 Signaturschlüssel für Ed25519 eingeführt, Google Cloud KMS bietet EC_SIGN_ED25519 an, manche HSM-Modelle und Azure Key Vault dagegen nicht. Wenn Tokens in einem verwalteten Schlüsselspeicher signiert werden müssen, prüfe zuerst dessen Liste der Algorithmen.
Wann welches Verfahren passt
- Öffentlicher Identity Provider, viele Relying Parties: ES256 (bei Bedarf zusätzlich RS256 für alte Empfänger).
- Schlüssel in HSM oder KMS über mehrere Clouds hinweg: ES256 für einheitliche Unterstützung.
- Interne Services, beide Seiten in deiner Hand, moderne Bibliotheken: Ed25519 mit
alg: Ed25519, wenn alle Verifizierer es unterstützen, sonstEdDSA. - Sendergebundene Tokens (DPoP) aus Browsern und mobilen Apps: ES256, das jeder Schlüsselspeicher einer Plattform als nicht exportierbaren Schlüssel erzeugen kann.
- Profile, die Algorithmen vorgeben (FAPI, Behörden, Finanzwesen): das, was das Profil auflistet.
Hinweise zur Migration
- Füge den neuen Schlüssel dem JWKS hinzu, mit eigenem
kidund explizitemalg, und gib den Empfängern Zeit, ihre Caches zu aktualisieren. - Konfiguriere die Verifizierer pro Schlüssel: Jeder Schlüssel akzeptiert genau einen Algorithmus (und bei Ed25519 beide Header-Werte,
EdDSAundEd25519, falls sich deine Empfänger unterscheiden). Akzeptiere nie denalgaus dem Token ohne diese Bindung. - Stelle den Aussteller auf den neuen Schlüssel um; vorhandene Tokens lassen sich weiter mit dem alten prüfen, bis sie ablaufen.
- Entferne den alten Schlüssel, sobald die längste Token-Laufzeit verstrichen ist.
- Teste die Kodierungen durchgängig: Eine ES256-Signatur aus einem KMS muss von DER ins Raw-Format umgewandelt werden, bevor sie in ein Token kommt; der Konverter DER ⇄ Raw und der JWT-Debugger zeigen, was ein Token enthält.
Quellen
- RFC 7518 §3.4, ES256 in JWS
- RFC 8037, CFRG-Kurven und EdDSA in JOSE
- RFC 9864, vollständig spezifizierte Algorithmen für JOSE und COSE
- RFC 8725, Best Current Practices für JWTs
- Referenz der Schlüsselspezifikationen in AWS KMS (ECC_NIST_EDWARDS25519)
- Schlüsselzwecke und Algorithmen in Google Cloud KMS
- FAPI 2.0 Security Profile §5.4.1, Kryptografie
- CVE-2022-21449 (Prüfung von ECDSA-Signaturen in Java)