By AndyPublished

ES256 Explained: ECDSA with P-256 for JWT Signing

ES256 is the JWT algorithm identifier for ECDSA using the P-256 curve and SHA-256. It's the modern asymmetric default, used by Apple Sign in with Apple, every WebAuthn/passkey credential, and most new OAuth 2.0 services. This guide explains what ES256 actually does, how the signature format works, why the keys are an order of magnitude smaller than RSA keys at equivalent security, and when ES256 is the right pick over RS256 or PS256.

What ES256 Means

The algorithm identifier ES256 appears in a JWT's header as"alg":"ES256". It decomposes into three parts that the JOSE specification fixes simultaneously:

  • ·E: ECDSA (Elliptic Curve Digital Signature Algorithm), defined in FIPS 186-4.
  • ·S: Signature (as opposed to encryption, JWE uses different identifiers).
  • ·256: SHA-256 hash function AND the P-256 curve (also called secp256r1 or prime256v1). The number pins both.

That last point trips people up. The "256" in ES256 is NOT a free choice of hash length over a curve — ES256 is exactly P-256 + SHA-256. ES384 is exactly P-384 + SHA-384. ES512 is P-521 (not 512) + SHA-512. RFC 7518 §3.4 specifies these triples explicitly; an implementation that pairs P-256 with SHA-384 isn't ES256, it's non-compliant.

The Signature Format That Catches Everyone Out

ECDSA signatures are mathematically pairs of integers, conventionally called (r, s). For ES256 each integer is 256 bits, 32 bytes. There are two ways the world encodes this pair:

  • ·ASN.1 DER encoding: what OpenSSL, Java, and most general-purpose crypto libraries produce by default. Variable-length (typically 70-72 bytes), wrapped in an ASN.1 SEQUENCE.
  • ·Raw IEEE P1363 / R || S concatenation: what JWT mandates. Fixed-length 64 bytes for ES256 (32 bytes r, 32 bytes s, big-endian, padded with leading zeros if needed). No ASN.1 wrapper.
The #1 ES256 implementation bug: signing produces a DER-encoded signature, but RFC 7515 requires the raw 64-byte concatenation. Tokens then fail verification in any standards-compliant decoder. If your ES256 token "verifies in one library but not another", check the signature length, 64 bytes raw vs 70-72 bytes DER is the giveaway.

The browser's Web Crypto API (which jwtdecode.app uses) produces and accepts the raw P1363 format by default for ECDSA algorithms, so the JWT spec aligns with the platform. Node.js's crypto.sign() defaults to DER and needs thedsaEncoding: 'ieee-p1363' option (Node 13+) to produce JWT-compliant output.

Key Format

ES256 uses an EC key pair on the P-256 curve. In PEM/SPKI form, a P-256 public key is ~178 bytes encoded, substantially smaller than a 2048-bit RSA SPKI public key (~294 bytes) and dramatically smaller than 4096-bit RSA (~550 bytes). The private key in PKCS#8 form is ~138 bytes.

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----

In JWK form, an ES256 public key has four fields: kty:"EC",crv:"P-256", plus the affine point coordinates xand y as base64url-encoded 32-byte values. The private key addsd, the scalar private key, also 32 bytes base64url-encoded.

Why Prefer ES256 over RS256

Smaller signatures and keys

An ES256 signature is 64 bytes raw, ~86 characters in base64url. An RS256 signature with a 2048-bit key is 256 bytes, ~342 characters base64url. For high-throughput services issuing many JWTs, ES256 saves real bandwidth, a JWT bearer header on every API call multiplies that delta.

Faster verification

ECDSA verification with P-256 is typically 2-5× faster than RSA-2048 verification on modern CPUs with hardware curve support. Signing is the opposite, RSA signing is faster than ECDSA signing — but for the issuer/verifier asymmetry of OAuth/OIDC where one IdP signs and many resource servers verify, the ES256 verification speedup is the path that matters.

Equivalent security at smaller parameters

NIST estimates P-256 provides ~128 bits of classical security, roughly equivalent to RSA-3072. A P-256 key is 8-12× smaller than an RSA key at equivalent strength. For embedded clients, mobile tokens, and constrained-bandwidth scenarios, the trade is decisive.

When ES256 Is Not the Right Choice

  • ·Legacy HSM constraints: older HSMs may not support ECDSA, or charge extra for elliptic-curve operations. Check before designing around it.
  • ·FIPS 140-2 compliance edge cases: some compliance regimes still favour RSA for institutional reasons unrelated to the underlying math. Confirm with your auditor.
  • ·Java 8 without BouncyCastle: out-of-the-box P-256 support exists, but ES256 JWT libraries on Java 8 historically had P1363/DER conversion bugs. Java 11+ is fine.
  • ·Nonce-reuse risk in your signing code: ECDSA requires a per-signature nonce. Reusing it (or using a weak RNG) leaks the private key entirely. RFC 6979 deterministic ECDSA eliminates this risk; check your library uses it. RSA-PSS has the same nonce concern but is more forgiving in failure modes.

ES256 vs PS256

Both are modern asymmetric JWT algorithms. The honest comparison: ES256 keys are smaller; PS256 interoperates with RSA-only infrastructure. If you're already paying the cost of RSA key infrastructure (HSMs, key rotation tooling, JWKS endpoints), PS256 is a solid choice over RS256 because PSS padding eliminates the malleability concerns of PKCS#1 v1.5. If you're greenfield, ES256 is generally the better default. See PS256 vs RS256 for the deeper RSA-side comparison.

Verifying an ES256 Token

Verification needs the issuer's public key (in PEM, JWK, or JWKS form), the algorithm identifier from the token header (confirmed to be ES256, never trust the header without checking against an allow-list), and the standard JWT verification steps: split on dots, base64url-decode the signature to 64 raw bytes, recompute the signing input as base64url(header) + "." + base64url(payload), SHA-256 the signing input, and verify the signature with the public key.

jwtdecode.app does this entirely in the browser via SubtleCrypto.verify("ECDSA"). Paste a token and a public key in either PEM or JWK form into the JWT decoder, verification runs locally, the token never leaves your browser.

Summary

ES256 is the recommended asymmetric JWT algorithm for new systems. It produces a 64-byte raw signature (not DER), uses P-256 curve and SHA-256 strictly per RFC 7518, and offers smaller keys, faster verification, and equivalent security to RSA-3072. The two pitfalls to watch: signature encoding (raw vs DER) and nonce generation in your signing library.

Ready to decode a token?
Use the free JWT decoder — paste any token for instant results, entirely in your browser.
Open JWT Decoder