Aarunya AppsAarunya Apps

ES256 JWT Algorithm

ES256 signs JWTs with an Elliptic Curve DSA (ECDSA) P-256 private key and verifies with the corresponding public key. ECDSA offers the same asymmetric verification model as RS256 — share the public key freely, keep the private key secret — but with dramatically smaller keys and faster operations. A 256-bit EC key provides security equivalent to a 3072-bit RSA key.

Family
ECDSA
Hash
SHA-256
Key type
asymmetric
Performance
Medium (0.1–1ms)

Key Requirements

EC key pair on the P-256 (secp256r1/prime256v1) curve. Generate: openssl ecparam -name prime256v1 -genkey -noout -out ec-private.pem && openssl ec -in ec-private.pem -pubout -out ec-public.pem

JWT Header

Every JWT using ES256 has this header (base64url-encoded as the first segment):

{
  "alg": "ES256",
  "typ": "JWT"
}

Code Examples

Node.js — Sign (jose library)
import { SignJWT, importPKCS8 } from 'jose'

// Load EC private key once at startup
const privateKey = await importPKCS8(process.env.EC_PRIVATE_KEY!, 'ES256')

export async function signToken(payload: Record<string, unknown>) {
  return new SignJWT(payload)
    .setProtectedHeader({ alg: 'ES256' })
    .setIssuedAt()
    .setExpirationTime('1h')
    .sign(privateKey)
}
Node.js — Verify (jose library)
import { jwtVerify, importSPKI } from 'jose'

const publicKey = await importSPKI(process.env.EC_PUBLIC_KEY!, 'ES256')

export async function verifyToken(token: string) {
  const { payload } = await jwtVerify(token, publicKey, {
    algorithms: ['ES256'],
  })
  return payload
}
Python — PyJWT
import jwt
from cryptography.hazmat.primitives import serialization

with open("ec-private.pem", "rb") as f:
    private_key = serialization.load_pem_private_key(f.read(), password=None)
with open("ec-public.pem", "rb") as f:
    public_key = serialization.load_pem_public_key(f.read())

# Sign
token = jwt.encode({"sub": "1234567890"}, private_key, algorithm="ES256")

# Verify
payload = jwt.decode(token, public_key, algorithms=["ES256"])

When to Use ES256

The modern preferred alternative to RS256 for new systems. Smaller tokens (ECDSA signatures are ~64 bytes vs ~256 bytes for RSA-2048), faster signature generation, and no PKCS#1 padding concerns. Used by Google, Cloudflare, Apple, and AWS for their JWT-based APIs. Ideal for mobile and IoT devices where bandwidth and battery matter.

Security Considerations

ECDSA requires a cryptographically secure random nonce per signature (the 'k' value). If k is ever reused or predictable, the private key can be recovered — this famously happened with PlayStation 3's ECDSA implementation. Modern libraries handle k generation correctly. The private key must be generated with the OS CSPRNG.

Related Algorithms

Standardised in RFC 7518 §3.4 — JSON Web Algorithms (JWA).

Decode a real JWT

Paste any JWT into the debugger to inspect the header, payload, and verify an ES256 signature.

Open JWT Debugger

Frequently Asked Questions

How does ECDSA compare to RSA for JWT signing?

ECDSA provides equivalent security to RSA with much smaller keys: ES256 (P-256, 256-bit key) ≈ RSA-3072. This means smaller JWT signatures, faster verification, and shorter key material. ES256 is the preferred modern choice when asymmetric signing is needed.

What elliptic curve does ES256 use?

ES256 uses the P-256 curve (also known as secp256r1 or prime256v1). ES384 uses P-384 (secp384r1), and ES512 uses P-521 (secp521r1 — note: P-521, not P-512). The curve is specified in the 'crv' field of the JWK.

Is ECDSA safe from the nonce-reuse attack?

Modern ECDSA implementations in JavaScript (Node.js crypto, WebCrypto) and reputable libraries use the OS CSPRNG for nonce generation — making nonce reuse extremely unlikely. The infamous PS3 ECDSA attack happened because Sony used a constant nonce, not a random one. Using the jose or jsonwebtoken library protects you from this.