ES256: ECDSA with P-256 and SHA-256 in a JWT
ES256 signs with an elliptic curve key on P-256 using SHA-256. It gives the same asymmetric split as RS256 with a much smaller key and a signature a fraction of the size.
At a glance
- Algorithm
- ES256, ECDSA using P-256 and SHA-256
- Registered in
- RFC 7518 section 3.4
- Implementation requirement
- Recommended plus, in the RFC 7518 section 3.1 table
- Key
- Elliptic curve key pair on P-256, also known as secp256r1 or prime256v1
- Signature size
- 64 bytes, R and S concatenated at 32 bytes each, 86 base64url characters
- Verified in the decoder
- Yes, in your browser, with a PEM public key. The curve is taken from the algorithm name.
In detail
A P-256 key gives roughly the security of a 3072 bit RSA key at a fraction of the size, and the difference shows in the token. An RS256 signature on a 2048 bit key is 342 base64url characters; an ES256 signature is 86. For tokens carried in a header or a cookie on every request, that is the most immediate practical argument for elliptic curve signing.
The signature format catches implementers out. JOSE requires the raw concatenation of R and S, each padded to 32 bytes, giving exactly 64 bytes. Many cryptographic libraries emit ECDSA signatures in DER, a variable length structure of about 70 bytes, and feeding that into a JWT produces a token that fails everywhere with a signature length that varies run to run. Conversion between the two is a fixed piece of code, not an option.
ECDSA also needs a unique, unpredictable random value for every signature. Reusing that value across two signatures with the same key lets anyone recover the private key from the two signatures alone, which is a real world failure and not a theoretical one. Use a maintained implementation and do not write the nonce generation yourself; the deterministic construction in RFC 6979 exists for exactly this reason.
The decoder verifies ES256 in the browser from a PEM public key, selecting P-256 because the algorithm name fixes the curve. If verification fails with a key you are sure of, the signature encoding is worth checking first: a DER encoded signature is the most common cause, and its length being anything other than 86 base64url characters gives it away.
Examples
Header: { "alg": "ES256", "typ": "JWT" }
// Correct for JOSE: raw R || S, 32 bytes each
// 64 bytes -> 86 base64url characters
// Wrong: DER encoded, variable length
30 45 02 20 <r bytes> 02 21 00 <s bytes>If the third part of the token is not 86 characters for ES256, the signature is very likely still in DER form.
Things that catch people out
- Passing a DER encoded signature straight into a JWT. JOSE wants raw R and S concatenated, and the length difference is the tell.
- Stripping leading zero bytes from R or S. Both are fixed width at 32 bytes, and trimming them shortens the signature and breaks verification.
- Reusing the per signature random value. Two signatures made with the same nonce and key expose the private key completely.
Try it: JWT Decoder
Paste a token to decode the header and payload, read the claims and expiry, and verify the signature in your browser. Nothing is uploaded.
JWT Decoder
LOCAL · Browser-OnlyDecode and inspect JSON Web Tokens without sending them to a server
Related JWT claims and algorithms
All jwt claims and algorithms →ES384: ECDSA with P-384 and SHA-384 in a JWT
ES384 signs a JWT with ECDSA on curve P-384 using SHA-384. Signature length, where the curve is mandated, and verification in your browser.
RS256: RSASSA-PKCS1-v1_5 with SHA-256 in a JWT
RS256 signs a JWT with an RSA private key and verifies with the public key. Key sizes, the RS256 to HS256 confusion attack, and verifying in your browser.
EdDSA: Edwards-curve Signatures in a JWT
EdDSA signs a JWT with Ed25519 or Ed448 under the OKP key type. What RFC 8037 defines, why it is deterministic, and why the decoder cannot verify it.
ES512: ECDSA with P-521 and SHA-512 in a JWT
ES512 signs a JWT with ECDSA on curve P-521 using SHA-512. Why the curve is 521 bits, what that does to signature length, and how to verify it.
none: The Unsecured JWT Algorithm
The none algorithm means a JWT carries no signature at all. How the alg none attack works, what to configure, and how the decoder handles such a token.
alg (Algorithm) Header Parameter in a JWT
The alg header names the algorithm used to sign a JWT. Why the accepted algorithms belong in configuration, and what the decoder does with the value.
Tools for this
- JWT Decoder Free online JWT decoder - decode and inspect JSON Web Tokens without sending them to a server