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
| ES256 | EdDSA / Ed25519 | |
|---|---|---|
alg no JOSE | ES256 (RFC 7518 §3.4) | Ed25519 (RFC 9864); o antigo EdDSA (RFC 8037), hoje obsoleto |
| Curva e hash | P-256 com SHA-256 | Edwards25519 com 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 |
| Assinatura no token | 64 bytes em r‖s bruto, 86 caracteres | 64 bytes, 86 caracteres |
| Token de exemplo (medido abaixo) | 232 caracteres | 234 caracteres (valor de alg mais longo) |
| Assinatura determinística | Não (nonce aleatório na maioria das bibliotecas) | Sim |
| Nível de exigência na JWA | Recomendado+ | Opcional |
| ID no COSE | −7 (ou −9 ESP256 na RFC 9864) | −19 (o antigo −8) |
| KMS na nuvem | AWS, Google, Azure: sim | AWS (desde novembro de 2025) e Google: sim; Azure: não |
| WebCrypto nos navegadores | Todos os navegadores | Firefox 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).
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
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: Ed25519se 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
- Adicione a chave nova ao JWKS com seu próprio
kide umalgexplícito, e dê tempo para os consumidores atualizarem os caches. - Configure os verificadores por chave: cada chave aceita exatamente um algoritmo (e, no caso do Ed25519, tanto o valor
EdDSAquanto oEd25519no cabeçalho, se seus consumidores divergirem). Nunca aceite oalgdo token sem esse vínculo. - Migre o emissor para a chave nova; os tokens existentes continuam sendo verificados com a antiga até expirarem.
- Remova a chave antiga depois de transcorrido o maior tempo de vida de um token.
- 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
- RFC 7518 §3.4, ES256 no JWS
- RFC 8037, curvas do CFRG e EdDSA no JOSE
- RFC 9864, algoritmos totalmente especificados para JOSE e COSE
- RFC 8725, boas práticas atuais para JWT
- Referência de especificações de chave do AWS KMS (ECC_NIST_EDWARDS25519)
- Finalidades de chave e algoritmos do Google Cloud KMS
- Perfil de segurança do FAPI 2.0, §5.4.1, criptografia
- CVE-2022-21449 (verificação de assinaturas ECDSA no Java)