ES256 (ECDSA P-256)
ES256 signs JWTs with ECDSA on the P-256 (secp256r1) curve using SHA-256, registered in RFC 7518 as the mainstream elliptic-curve option for JSON Web Tokens. Compared to RS256, ES256 delivers comparable security with dramatically smaller keys and signatures—roughly 64-byte ECDSA signatures versus 256-byte RSA outputs at 2048-bit security levels. Platform teams choose ES256 for mobile backends, IoT gateways, and high-volume APIs where bandwidth and JWKS payload size matter, provided their language libraries and HSM support secp256r1. Implementation mirrors RS256 asymmetrically: one private key signs, many services verify with the public key or JWKS, but PEM encoding and signature malleability handling require extra care.
Last updated July 25, 2026
How It Works
ES256 uses the standard three-part JWT layout. The header declares alg ES256; the payload carries claims; the signature is ECDSA over the SHA-256 hash of header.payload using a P-256 private scalar. Verifiers decode the header, confirm the curve algorithm, load the public point (often from EC PEM or a JWK with crv P-256, x, and y coordinates), and validate the DER or raw R||S signature components according to the library JWT profile. RFC 7518 specifies ES256 as ECDSA using P-256 and SHA-256; RFC 7517 maps elliptic keys into JWK form for JWKS publication alongside RSA keys. ECDSA signatures are non-deterministic unless you implement RFC 6979 deterministic nonce generation—most JWT libraries handle this internally, but test against known vectors when porting to new runtimes. As with RSA, include kid when rotating keys and never expose private EC PEM to verifiers. Middleware must pin algorithms: ['ES256'] because accepting ES384 or HS256 interchangeably invites confusion attacks. After cryptographic verification, enforce exp, iss, aud, and custom claims. Generate keys with openssl ecparam -genkey -name prime256v1 or the asymmetric preset on /tools/rsa-key-generator, store private material in KMS, and publish the public JWK set to the same JWKS URL pattern used for RS256 deployments.
When to Use
Pick ES256 when you need asymmetric JWT signing but want smaller signatures and JWKS documents than RS256 provides. It suits mobile-first APIs, edge CDNs validating tokens at scale, and greenfield platforms where modern libraries already support P-256. ES256 is also attractive when your HSM or cloud KMS natively exposes EC keys with lower operational cost than RSA.
When Not to Use
Avoid ES256 when legacy integrators only support RS256, when your verification toolchain lacks mature ECDSA JWT handling, or when compliance mandates RSA specifically. Do not adopt ES256 without testing signature encoding quirks across every consumer language—partial ECDSA implementations have caused production outages. If team familiarity and vendor support favor RSA today, RS256 remains a fully acceptable choice over forcing ES256 prematurely.
Key Length Requirements
ES256 uses the P-256 elliptic curve, providing approximately 128 bits of security strength with a 256-bit private scalar and a public key represented as an uncompressed point (65 bytes raw) or compressed form (33 bytes). This is comparable to 3072-bit RSA security while producing JWT signatures around 64 bytes Base64URL-encoded. Private keys are typically stored as SEC1 or PKCS#8 EC PEM blocks; public keys export as SPKI PEM or JWK with crv P-256. Do not confuse ES256 (P-256) with ES384 (P-384) or ES512 (P-521)—each uses a different curve and must not be interchangeable in verify allowlists. Rotate keys by generating a new EC pair, updating JWKS with a new kid, and retiring the previous public key after token TTL elapses.
Security Pitfalls
- Reusing RSA-oriented tooling that mislabels or truncates EC PEM files, leading to silent sign failures or keys that never rotate correctly.
- Allowing multiple ECDSA algorithms in one permissive verify call such as algorithms: ['ES256', 'ES384', 'HS256'], which opens cross-algorithm substitution attacks.
- Assuming smaller signatures mean weaker security and compensating by embedding sensitive claims in JWT payloads instead of keeping them server-side.
- Publishing JWKS entries missing crv, x, or y fields, causing spec-compliant validators to reject all ES256 tokens from your issuer.
- Ignoring signature malleability protections in custom verifiers that compare raw R and S bytes without delegating to audited library code.
Code Examples
Node.js — ES256 with EC PEM key pair
const fs = require('fs');
const jwt = require('jsonwebtoken');
const privateKey = fs.readFileSync('ec-private.pem');
const publicKey = fs.readFileSync('ec-public.pem');
const token = jwt.sign(
{ sub: 'user_123', tier: 'pro' },
privateKey,
{
algorithm: 'ES256',
keyid: 'ec-2026-01',
expiresIn: '30m',
issuer: 'https://auth.example.com',
}
);
const payload = jwt.verify(token, publicKey, {
algorithms: ['ES256'],
issuer: 'https://auth.example.com',
});
console.log(payload.sub);Go — sign and verify with golang-jwt
package main
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"os"
"time"
"github.com/golang-jwt/jwt/v5"
)
func main() {
priv := loadECPrivateKey("ec-private.pem")
pub := &priv.PublicKey
token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{
"sub": "user_123",
"exp": time.Now().Add(30 * time.Minute).Unix(),
"iss": "https://auth.example.com",
})
token.Header["kid"] = "ec-2026-01"
signed, _ := token.SignedString(priv)
parsed, _ := jwt.Parse(signed, func(t *jwt.Token) (interface{}, error) {
return pub, nil
}, jwt.WithValidMethods([]string{"ES256"}))
_ = parsed
}Generate Keys
Recommended: asymmetric
Open Rsa Key GeneratorRelated Comparisons
Related Terms
Related Guides
Frequently Asked Questions
Should I choose ES256 or RS256 for a new API?
Both are secure asymmetric choices. ES256 offers smaller keys and signatures, which helps mobile clients and JWKS caching at scale. RS256 has broader legacy support in older enterprise middleware and hardware tokens. Benchmark verify latency in your stack and confirm every consumer library handles EC PEM and JWK fields before committing.
Why are ES256 signatures shorter than RS256 signatures?
ECDSA on P-256 produces two 256-bit integers R and S, encoded compactly to about 64 bytes, while 2048-bit RSA signatures are modulus-sized—roughly 256 bytes. The underlying security level is comparable even though the on-the-wire footprint differs. Shorter signatures reduce header overhead in cookies and mTLS-adjacent transports but do not justify stuffing sensitive data into JWT claims.
How do I expose ES256 public keys in JWKS?
Publish JWK entries with kty EC, crv P-256, x, y, use sig, alg ES256, and a unique kid per key version. Verifiers map the token header kid to the matching JWK before calling ECDSA verify. The same /.well-known/jwks.json endpoint can mix ES256 and RS256 keys if you operate hybrid issuers during migration.
What PEM formats do ES256 libraries expect?
Most Node, Go, and Java JWT libraries accept PKCS#8 or SEC1 EC private PEM files and SPKI public PEM for verification. OpenSSL generates prime256v1 keys with openssl ecparam -genkey -name prime256v1. Ensure line endings and BEGIN EC PRIVATE KEY headers remain intact when copying into secrets managers—see glossary pem-encoding for encoding details shared with RSA deployments.
Looking for a short definition?
Glossary: ES256 (ECDSA-SHA256)