ES256 frente a EdDSA para JWT es una cuestión más acotada que ECDSA frente a EdDSA en general. Los tokens tienen el mismo tamaño, ambos son rápidos y ambos se recomiendan en lugar de RS256 para emisores nuevos. Lo que cambia es la compatibilidad: muchas plataformas y almacenes de claves exigen o esperan ES256, mientras que Ed25519 es más fácil de implementar correctamente y en JOSE ahora se llama Ed25519 en lugar de EdDSA.
La guía ES256 frente a RS256 cubre la parte RSA de la misma decisión; esta página compara las dos opciones de curva elíptica.
De un vistazo
| ES256 | EdDSA / Ed25519 | |
|---|---|---|
alg de JOSE | ES256 (RFC 7518 §3.4) | Ed25519 (RFC 9864); el antiguo EdDSA (RFC 8037), ahora obsoleto |
| Curva y hash | P-256 con SHA-256 | Edwards25519 con SHA-512 |
| JWK | kty: EC, crv: P-256, x, y | kty: OKP, crv: Ed25519, x |
| JWK pública (medida, JSON compacto) | 126 caracteres | 79 caracteres |
| Firma en el token | 64 bytes r‖s en bruto, 86 caracteres | 64 bytes, 86 caracteres |
| Token de ejemplo (medido más abajo) | 232 caracteres | 234 caracteres (valor de alg más largo) |
| Determinista | No (nonce aleatorio en la mayoría de las bibliotecas) | Sí |
| Nivel de requisito en JWA | Recomendado+ | Opcional |
| Identificador COSE | −7 (o −9 ESP256 en el RFC 9864) | −19 (antes, −8) |
| KMS en la nube | AWS, Google, Azure: sí | AWS (desde noviembre de 2025) y Google: sí; Azure: no |
| WebCrypto en el navegador | Todos los navegadores | Firefox 129, Safari 17, Chrome 137 y posteriores |
Tamaño y velocidad
Ambas firmas ocupan 64 bytes, así que los tokens tienen la misma longitud salvo por el encabezado. El script siguiente, ejecutado con Node.js 26.10, firma los mismos claims (declaraciones) de las dos formas usando solo node:crypto (el código real debería usar una biblioteca 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 velocidad no es un factor decisivo: en un núcleo de un Apple M4 Max, OpenSSL 3.6.5 produce unas 87.000 firmas P-256 y 60.000 firmas Ed25519 por segundo. Un JWKS con claves Ed25519 es más pequeño porque las claves OKP tienen una sola coordenada.
Codificación e implementación correcta
Las firmas ES256 en JWS deben tener la forma r‖s en bruto de 64 bytes (RFC 7518 §3.4). Muchas API de ECDSA usan DER por defecto: OpenSSL, SHA256withECDSA de Java, ecdsa.SignASN1 de Go, crypto.sign() de Node sin dsaEncoding y las API de firma de AWS KMS y Google Cloud KMS. El código ES256 escrito a mano que olvida la conversión produce firmas de 70 a 72 bytes que los verificadores conformes rechazan, y el error contrario rompe la verificación de tokens correctos. Los verificadores ECDSA también deben rechazar r o s iguales a cero; las versiones 15 a 18 de Java no lo hacían (CVE-2022-21449), lo que permitía a cualquiera falsificar tokens ES256 hasta que se aplicó el parche.
Ed25519 tiene una única codificación de 64 bytes, firma determinista y ningún nonce que se pueda generar mal. Sus trampas están en otra parte: algunas bibliotecas tratan la clave privada como una semilla de 32 bytes y otras como 64 bytes, y las bibliotecas JOSE más antiguas solo reconocen "alg": "EdDSA".
Cambio de nombre del alg
El RFC 8037 (2017) registró EdDSA como un algoritmo JOSE polimórfico cuya curva viene determinada por la clave (crv: Ed25519 o Ed448). El RFC 9864 (octubre de 2025) declara obsoletos los identificadores polimórficos y registra Ed25519 y Ed448, de modo que el alg por sí solo determina la operación, como ya ocurría con ES256. La compatibilidad es desigual en 2026: jose 6 acepta tanto EdDSA como Ed25519, joserfc y Nimbus JOSE+JWT admiten los nombres nuevos, mientras que PyJWT 2.15 y golang-jwt v5 siguen aceptando solo EdDSA. Si los tokens los consumen muchas partes, comprueba cada consumidor antes de cambiar el valor del encabezado; durante una transición, los verificadores pueden aceptar ambos nombres para la misma clave.
Compatibilidad con plataformas y almacenes de claves
ES256 es el mínimo común denominador. Es la opción habitual para las pruebas DPoP (RFC 9449), uno de los tres algoritmos que permite FAPI 2.0 (PS256, ES256 o EdDSA con Ed25519), obligatorio para la App Store Server API de Apple y para los tokens de proveedor de APNs, admitido por todos los proveedores de OpenID Connect y el algoritmo COSE predeterminado en WebAuthn. Todos los HSM, TPM, Secure Enclave y KMS en la nube ofrecen claves P-256.
En software, Ed25519 ya está a la altura: Node.js, Go, Java 15 y posteriores, .NET mediante bibliotecas, cryptography de Python, WebCrypto en el navegador y las principales bibliotecas JOSE. En los almacenes de claves es más reciente: AWS KMS añadió claves de firma Ed25519 en noviembre de 2025 y Google Cloud KMS ofrece EC_SIGN_ED25519, mientras que algunos modelos de HSM y Azure Key Vault no lo admiten. Si los tokens deben firmarse dentro de un almacén de claves gestionado, consulta primero su lista de algoritmos.
Cuándo elegir cada uno
- Proveedor de identidad público con muchas relying parties (las partes que confían en sus tokens): ES256 (manteniendo RS256 para los consumidores antiguos, si hace falta).
- Claves en HSM o KMS de varias nubes: ES256, por su compatibilidad uniforme.
- Servicios internos, con ambos extremos bajo tu control y bibliotecas modernas: Ed25519 con
alg: Ed25519si todos los verificadores lo admiten; si no,EdDSA. - Tokens vinculados al remitente (DPoP) desde navegadores y aplicaciones móviles: ES256, cuyas claves puede generar como no exportables el almacén de claves de cualquier plataforma.
- Perfiles que fijan los algoritmos (FAPI, administraciones públicas, sector financiero): lo que indique el perfil.
Notas de migración
- Añade la clave nueva al JWKS con su propio
kidy unalgexplícito, y da tiempo a los consumidores para que actualicen sus cachés. - Configura los verificadores por clave: cada clave acepta exactamente un algoritmo (y, en el caso de Ed25519, los dos valores de encabezado
EdDSAyEd25519si tus consumidores difieren). Nunca aceptes elalgdel token sin esa vinculación. - Haz que el emisor firme con la clave nueva; los tokens existentes se siguen verificando con la antigua hasta que caducan.
- Retira la clave antigua cuando haya transcurrido la vida útil máxima de los tokens.
- Prueba las codificaciones de extremo a extremo: una firma ES256 obtenida de un KMS debe convertirse de DER a formato en bruto antes de incluirla en un token; el conversor entre DER y r‖s en bruto y el decodificador de JWT muestran qué contiene un token.
Referencias
- RFC 7518 §3.4, ES256 en JWS
- RFC 8037, curvas de CFRG y EdDSA en JOSE
- RFC 9864, algoritmos totalmente especificados para JOSE y COSE
- RFC 8725, buenas prácticas actuales para JWT
- Referencia de especificaciones de clave de AWS KMS (ECC_NIST_EDWARDS25519)
- Propósitos de clave y algoritmos de Google Cloud KMS
- Perfil de seguridad FAPI 2.0 §5.4.1, criptografía
- CVE-2022-21449 (verificación de firmas ECDSA en Java)