Pular para o conteúdo
ecdsa.com

ES256 vs EdDSA (Ed25519) para JWT

Os dois produzem assinaturas de 64 bytes e tokens quase idênticos. A decisão depende de quais bibliotecas, repositórios de chaves e consumidores oferecem suporte a cada um, e do novo nome de alg, Ed25519.

ES256 vs EdDSA para JWT é uma questão mais restrita que ECDSA vs EdDSA em geral. Os tokens têm o mesmo tamanho, os dois são rápidos e os dois são recomendados no lugar do RS256 para emissores novos. O que muda é o suporte: muitas plataformas e repositórios de chaves exigem ou esperam o ES256, enquanto o Ed25519 é mais simples de implementar corretamente e agora se chama Ed25519, e não EdDSA, no JOSE.

O guia ES256 vs RS256 trata do lado RSA da mesma decisão; esta página compara as duas opções de curva elíptica.

Visão geral

ES256EdDSA / Ed25519
alg no JOSEES256 (RFC 7518 §3.4)Ed25519 (RFC 9864); o antigo EdDSA (RFC 8037), hoje obsoleto
Curva e hashP-256 com SHA-256Edwards25519 com SHA-512
JWKkty: EC, crv: P-256, x, ykty: OKP, crv: Ed25519, x
JWK pública (medida, JSON compacto)126 caracteres79 caracteres
Assinatura no token64 bytes em r‖s bruto, 86 caracteres64 bytes, 86 caracteres
Token de exemplo (medido abaixo)232 caracteres234 caracteres (valor de alg mais longo)
Assinatura determinísticaNão (nonce aleatório na maioria das bibliotecas)Sim
Nível de exigência na JWARecomendado+Opcional
ID no COSE−7 (ou −9 ESP256 na RFC 9864)−19 (o antigo −8)
KMS na nuvemAWS, Google, Azure: simAWS (desde novembro de 2025) e Google: sim; Azure: não
WebCrypto nos navegadoresTodos os navegadoresFirefox 129, Safari 17, Chrome 137 e posteriores

Tamanho e velocidade

As duas assinaturas têm 64 bytes, então os tokens têm o mesmo comprimento, exceto pelo cabeçalho. O script abaixo, executado com o Node.js 26.10, assina as mesmas claims das duas formas usando apenas node:crypto (em código real, convém usar uma biblioteca JOSE).

Node.js: JWTs ES256 e Ed25519 com o mesmo payload
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");
Saída
ES256   token 232 chars, public JWK 126 chars
Ed25519 token 234 chars, public JWK 79 chars

A velocidade não é um fator decisivo: em um núcleo do Apple M4 Max, o OpenSSL 3.6.5 produz cerca de 87.000 assinaturas P-256 e 60.000 assinaturas Ed25519 por segundo. Um JWKS com chaves Ed25519 é menor porque as chaves OKP têm uma única coordenada.

Codificação e corretude

No JWS, as assinaturas ES256 precisam estar no formato r‖s bruto de 64 bytes (RFC 7518 §3.4). Muitas APIs de ECDSA usam DER por padrão: o OpenSSL, o SHA256withECDSA do Java, o ecdsa.SignASN1 do Go, o crypto.sign() do Node sem dsaEncoding e as APIs de assinatura do AWS KMS e do Google Cloud KMS. Código ES256 escrito à mão que esquece a conversão produz assinaturas de 70 a 72 bytes, que os verificadores em conformidade com a norma rejeitam, e o erro oposto quebra a verificação de tokens corretos. Os verificadores ECDSA também precisam rejeitar r ou s iguais a zero; o Java, das versões 15 a 18, não fazia isso (CVE-2022-21449), o que permitia a qualquer pessoa forjar tokens ES256 até a correção ser aplicada.

O Ed25519 tem uma única codificação de 64 bytes e assinatura determinística, sem nonce que possa sair errado. As armadilhas dele ficam em outro ponto: algumas bibliotecas tratam a chave privada como uma semente de 32 bytes e outras como 64 bytes, e bibliotecas JOSE mais antigas só reconhecem "alg": "EdDSA".

O nome do alg mudou

A RFC 8037 (2017) registrou o EdDSA como um algoritmo JOSE polimórfico, cuja curva é determinada pela chave (crv: Ed25519 ou Ed448). A RFC 9864 (outubro de 2025) torna obsoletos os identificadores polimórficos e registra Ed25519 e Ed448, de modo que o alg sozinho determina a operação, como sempre aconteceu com o ES256. Em 2026, o suporte é desigual: o jose 6 aceita tanto EdDSA quanto Ed25519, o joserfc e o Nimbus JOSE+JWT têm suporte aos nomes novos, enquanto o PyJWT 2.15 e o golang-jwt v5 ainda aceitam apenas EdDSA. Para tokens consumidos por muitas partes, confira cada consumidor antes de trocar o valor no cabeçalho; durante uma transição, os verificadores podem aceitar os dois nomes para a mesma chave.

Suporte em plataformas e repositórios de chaves

O ES256 é o mínimo denominador comum. É a escolha usual para as provas DPoP (RFC 9449), um dos três algoritmos que o FAPI 2.0 permite (PS256, ES256 ou EdDSA com Ed25519), obrigatório na App Store Server API da Apple e nos tokens de provedor do APNs, aceito por todos os provedores OpenID Connect e o algoritmo COSE padrão no WebAuthn. Todo HSM, TPM, Secure Enclave e KMS na nuvem oferece chaves P-256.

No software, o Ed25519 já recuperou o atraso: Node.js, Go, Java 15 e posteriores, .NET por meio de bibliotecas, o cryptography do Python, a WebCrypto dos navegadores e as principais bibliotecas JOSE. Nos repositórios de chaves, ele é mais recente: o AWS KMS adicionou chaves de assinatura Ed25519 em novembro de 2025 e o Google Cloud KMS oferece EC_SIGN_ED25519, enquanto alguns modelos de HSM e o Azure Key Vault não oferecem. Se os tokens precisam ser assinados dentro de um repositório de chaves gerenciado, confira antes a lista de algoritmos dele.

Quando escolher cada um

  • Provedor de identidade público, muitas partes confiáveis (relying parties): ES256 (mantendo o RS256 para consumidores antigos, se necessário).
  • Chaves em HSM ou KMS em várias nuvens: ES256, pelo suporte uniforme.
  • Serviços internos, com as duas pontas sob seu controle e bibliotecas modernas: Ed25519 com alg: Ed25519 se todos os verificadores tiverem suporte a ele; caso contrário, EdDSA.
  • Tokens restritos ao remetente (DPoP) de navegadores e aplicativos móveis: ES256, cuja chave o keystore de qualquer plataforma consegue gerar como não exportável.
  • Perfis que especificam algoritmos (FAPI, governo, setor financeiro): o que o perfil listar.

Notas de migração

  1. Adicione a chave nova ao JWKS com seu próprio kid e um alg explícito, e dê tempo para os consumidores atualizarem os caches.
  2. Configure os verificadores por chave: cada chave aceita exatamente um algoritmo (e, no caso do Ed25519, tanto o valor EdDSA quanto o Ed25519 no cabeçalho, se seus consumidores divergirem). Nunca aceite o alg do token sem esse vínculo.
  3. Migre o emissor para a chave nova; os tokens existentes continuam sendo verificados com a antiga até expirarem.
  4. Remova a chave antiga depois de transcorrido o maior tempo de vida de um token.
  5. Teste as codificações de ponta a ponta: uma assinatura ES256 vinda de um KMS precisa ser convertida de DER para o formato bruto antes de entrar no token; o conversor DER ⇄ bruto e o depurador de JWT mostram o que um token contém.

Referências

Todas as comparações