When you generate an RSA-2048 key pair, you get a private key that's roughly 1700 bytes. The equivalent security from an elliptic curve key — P-256 — is a private key of just 121 bytes. Same security level, 14× smaller. That size difference propagates into every JWT you sign, every TLS handshake, and every API call that carries a public key.
This post explains how EC keys work, which curve to pick, and how JWT ES256/ES384/ES512 compare to the RSA RS256/RS384/RS512 family.
What Makes Elliptic Curves Different
Classical RSA security relies on the difficulty of factoring large integers. To make factoring hard enough, you need very large numbers — 2048 bits is the current minimum recommendation, and 4096 bits for long-term security.
Elliptic curve cryptography (ECC) relies on a different hard problem: the elliptic curve discrete logarithm problem (ECDLP). It's computationally harder per bit than integer factorization, which is why you get equivalent security with much smaller keys.
| RSA key size | Equivalent EC curve | Security bits |
|---|---|---|
| 1024-bit | — (too weak) | 80 |
| 2048-bit | P-256 | 128 |
| 3072-bit | P-256 | 128 |
| 7680-bit | P-384 | 192 |
| 15360-bit | P-521 | 256 |
P-256 gives you the same 128-bit security as a 3072-bit RSA key, at roughly 1/25th the size.
EC vs RSA — same security, dramatically smaller keys
The Three NIST Curves: P-256, P-384, P-521
The Web Crypto API (and most cryptographic libraries) support three NIST-standardized curves:
P-256 (secp256r1)
- 128-bit security
- JWT algorithm: ES256 (ECDSA with SHA-256)
- The right default for almost every application
- Supported everywhere: browsers, Node.js, Go, Rust, Java, Python
P-384 (secp384r1)
- 192-bit security
- JWT algorithm: ES384 (ECDSA with SHA-384)
- Required in some US government and financial compliance contexts (CNSA Suite)
- Slightly slower than P-256, but not noticeably so
P-521 (secp521r1)
- 256-bit security (note: 521 bits, not 512)
- JWT algorithm: ES512 (ECDSA with SHA-512)
- Overkill for most applications; 521-bit math can be awkward on hardware without native 64-bit words
- Use only if you have a specific long-term archival security requirement
For web APIs, microservices, and mobile authentication, P-256 is the correct choice.
EC Keys in JWT: ES256 vs RS256
Both ES256 and RS256 sign JWTs. The difference is in the algorithm used to produce the signature:
- RS256: RSA with SHA-256, PKCS#1 v1.5 padding
- ES256: ECDSA with SHA-256 on P-256
The signed JWT is smaller with ES256 because the signature itself is smaller. An RS256 signature with a 2048-bit RSA key is 256 bytes (Base64-encoded: ~344 chars). An ES256 signature on P-256 is 64 bytes (~86 chars). This matters in HTTP headers and cookies.
Key distribution
ECDSA signatures require both the signing key and the verification key. If you're distributing public keys via JWKS (a JSON Web Key Set endpoint), EC public keys in JWK format are significantly more compact than RSA public keys.
An RSA-2048 public key in JWK has n (256 bytes, Base64url-encoded: 342 chars) and e. An EC P-256 public key in JWK has x and y (32 bytes each, 43 chars each). Much more bandwidth-friendly at scale.
Code: Signing and Verifying with ES256
PEM vs JWK Format
When you generate an EC key pair, you'll typically need the keys in one of two formats:
PEM is the format used by OpenSSL, most TLS stacks, and server-side key storage:
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----
JWK (JSON Web Key) is the format used by JOSE libraries, JWKS endpoints, and frontend key handling:
{
"kty": "EC",
"crv": "P-256",
"x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
"y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
"d": "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI"
}The d field in JWK is the private scalar — keep it secret just like a PEM private key.
ECDSA Signatures Are Randomized — Understand the Implication
Unlike RSA-PKCS#1, ECDSA signatures require a random nonce (k) per signature. Signing the same message twice will produce different signatures. This is expected and correct.
The critical security requirement: the nonce must be cryptographically random and never reused. A reused nonce with the same private key leaks the private key — this is exactly how the PlayStation 3 private key was recovered in 2010. Implementations in the Web Crypto API and established libraries handle this correctly, but it's important to understand when evaluating custom cryptographic code.
Generating EC Key Pairs in the Browser
The Web Crypto API supports ECDSA natively:
You can generate P-256, P-384, or P-521 keys directly in your browser with the EC Key Generator — no installation required.
When to Use RSA Instead
EC keys are better in almost every dimension, but there are cases where RSA is still the right call:
- Legacy system compatibility: some older Java and .NET versions have limited EC support
- RSA-OAEP encryption: EC keys are for signing only (ECDSA) or key exchange (ECDH). If you need asymmetric encryption (encrypt a message with a public key, decrypt with private), you need RSA-OAEP — there's no ECDSA equivalent for encryption
- HSM / smart card constraints: some hardware security modules have RSA-only firmware
If you're building something new with full library control, P-256 + ES256 is the better default over RSA-2048 + RS256.
- P-256 gives 128-bit security at 1/25th the key size of equivalent RSA
- ES256 (ECDSA + P-256 + SHA-256) is the recommended JWT signing algorithm for new systems
- Use PEM for server-side key storage, JWK for JWKS endpoints and frontend distribution
- The Web Crypto API supports P-256, P-384, and P-521 natively — no libraries needed
- ECDSA is signing only; use RSA-OAEP if you need asymmetric encryption
→ Generate EC key pairs instantly with the EC Key Generator