JWKS & JWK Decoder
What Is a JWKS?
A JSON Web Key Set is a JSON document containing an array of public keys, published so that anyone verifying a token can fetch the key that signed it. Identity providers serve one at a well-known URL — commonly https://issuer.example.com/.well-known/jwks.json — and it is the mechanism that makes token verification work at scale without distributing keys by hand.
Each entry is a JSON Web Key: a JSON object describing one key's type, intended use, and the raw mathematical parameters. For RSA that means a modulus n and a public exponent e; for elliptic curve keys it means a curve name and the point coordinates x and y. All of these are base64url-encoded big-endian integers, which is why a JWK looks like an opaque wall of characters until something decodes it.
JWK Decoder: Inspect a Single JSON Web Key
You do not need a whole key set. Paste one JWK — a single object with a kty member, without the surrounding {"keys": [...]} wrapper — and the tool decodes it the same way, reporting "1 key in this set". A bare JSON array of JWKs works too. That covers the usual places a lone key turns up: a key copied out of an identity provider's dashboard, the jwk header of a DPoP proof, a key embedded in a config file, or one entry you have cut out of a larger JWKS to look at on its own.
For example, the elliptic curve key from the Load Example set, pasted by itself:
{"kty":"EC","kid":"ec-2026-08","use":"sig","alg":"ES256","crv":"P-256",
"x":"Fmkf6gOy2QgDN2cI3S-7pLuRUFZ_lMpQKlWjG_Ss20I",
"y":"C_4rLiAm9uoD2PWSb8YZqSxRT6OgA_209SMRkHdQQ_I"}
decodes to kty EC, use sig — signature verification, curve P-256 — pairs with SHA-256, the RFC 7638 thumbprint 3PyRZ6oMyotwyVNlo73OKFcVvrZ9CkaDLABq9WINQps, and this public key:
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFmkf6gOy2QgDN2cI3S+7pLuRUFZ/ lMpQKlWjG/Ss20IL/isuICb26gPY9ZJvxhmpLFFPo6AD/bT1IxGQd1BD8g== -----END PUBLIC KEY-----
If the single key you paste is a private JWK — it contains d — the tool warns about the private material and still converts only the public half to PEM, so you can safely extract the public key to hand to a verifier.
JWK vs JWKS vs JWT
The three names are one letter apart and routinely confused. They are different things that work together:
| Term | What it is | Looks like |
|---|---|---|
| JWK | One key, as a JSON object (RFC 7517) | {"kty":"RSA","n":"…","e":"AQAB"} |
| JWKS | A set of JWKs, published by an issuer | {"keys":[{…},{…}]} |
| JWT | A signed token that a key in the set verifies | eyJhbGciOi… — three base64url parts joined by dots |
The link between them is the kid: the JWT header names a kid, and the verifier picks the JWK with that kid out of the JWKS. Paste a token into the JWT decoder to read its header, then find the matching key here.
Why a Key Set Holds Several Keys
A key set contains more than one key so the issuer can rotate signing keys without breaking tokens already in circulation. During a rotation the issuer publishes the new public key alongside the old one, starts signing with the new key, and removes the old one only after every token signed with it has expired. Consumers that re-fetch the key set keep verifying throughout.
The kid field is what makes this work. It labels each key, and every token signed by that key carries the same kid in its header, so a verifier knows exactly which key to use rather than guessing by trial. A key set whose entries lack kid values forces verifiers to attempt every key in turn — this tool flags that, because it is a design smell rather than a fatal error.
The Fields You Will See
| Field | Meaning |
|---|---|
| kty | Key type — RSA, EC, OKP (Edwards curves), or oct (symmetric) |
| kid | Key identifier, matched against the kid in a token header |
| use | sig for signature verification, enc for encryption |
| alg | The algorithm this key is intended for, such as RS256 or ES256 |
| n / e | RSA modulus and public exponent. AQAB decodes to 65537, the near-universal exponent |
| crv / x / y | Elliptic curve name and public point coordinates |
| x5c | An X.509 certificate chain, if the issuer publishes one |
| d, p, q | Private parameters. These must never appear in a published key set |
d, treat the key as compromised. The d parameter is the private exponent. A JWKS is a public document, so a private parameter appearing in one means the signing key has been published to anyone who requested the URL. This tool raises a warning when it sees private material, but the only real remedy is to rotate the key immediately.
Converting a JWK to PEM
Most command-line tooling — OpenSSL, and the majority of server-side libraries outside the JavaScript ecosystem — expects a PEM-encoded key rather than a JWK. This tool performs that conversion by importing the JWK through the Web Crypto API and exporting it in SPKI form, which is the structure inside a BEGIN PUBLIC KEY block. Because the browser's own cryptographic implementation does the encoding, the output is the same DER structure OpenSSL would produce.
Note the distinction between key formats that look similar. BEGIN PUBLIC KEY is SPKI and carries an algorithm identifier alongside the key material. BEGIN RSA PUBLIC KEY is PKCS#1 and holds only the RSA numbers. Libraries are usually specific about which they accept, and supplying the wrong one produces an unhelpful parse error rather than a clear message.
JWKS to PEM: Convert Every Key in a Set
Paste a full JWKS and every public key gets its own PEM block and Copy PEM button, labelled by its kid, so converting a whole set takes one paste rather than one conversion per key. RSA, EC (P-256, P-384, P-521) and Ed25519 keys all convert. Symmetric oct keys cannot, because they have no public half; the tool says so instead of producing output.
To do the same in a script or a build step, Node's built-in crypto module reads JWKs directly, and ignores metadata members such as kid, use and alg:
import { createPublicKey } from 'node:crypto';
import { readFileSync } from 'node:fs';
const { keys } = JSON.parse(readFileSync('jwks.json', 'utf8'));
for (const jwk of keys) {
const pem = createPublicKey({ key: jwk, format: 'jwk' })
.export({ type: 'spki', format: 'pem' });
console.log(`# ${jwk.kid}\n${pem}`);
}
Run against the Load Example set, that prints the same two BEGIN PUBLIC KEY blocks this page shows — the output of this tool was checked against Node's for hundreds of generated RSA, EC and Ed25519 keys, byte for byte.
Key Thumbprints (RFC 7638)
A JWK thumbprint is a SHA-256 hash over a canonical form of the key containing only its required members, sorted lexicographically with no whitespace. The canonical form matters as much as the hash: one extra space or a reordered member changes every character of the digest, which you can watch happen in our SHA-256 hash generator. It gives a key a stable identifier derived from the key material itself, so the same key produces the same thumbprint regardless of which optional fields a particular system attaches to it.
This is genuinely useful when tracking a key across environments. If staging and production disagree about whether a token should verify, comparing thumbprints answers "is this actually the same key?" without eyeballing a 2048-bit modulus. Some issuers also use the thumbprint as the kid value, which makes key identity self-describing.
Reading RSA Key Strength
The modulus length reported here is derived from the decoded byte length of n. A 2048-bit modulus is the current baseline for RSA signing keys, and 3072 or 4096 bits are common where a longer protection horizon is wanted. Keys below 2048 bits are flagged: 1024-bit RSA is deprecated and should not appear in a key set serving production traffic.
Elliptic curve keys achieve comparable strength with far smaller parameters, which is why ES256 signatures are a fraction of the size of RS256 signatures. If token size matters — and it does when every request carries one in a header — EC keys are worth considering. One naming trap is worth remembering: ES512 uses curve P-521, not a curve called P-512.
Debugging a Failing Verification
When a token that should verify does not, work through the key set in this order:
- Does the token's
kidexist in the key set? If not, the issuer has rotated keys and your cached copy is stale. This is by far the most common cause. Our JWT decoder shows the headerkidwithout needing any key. - Does
ktymatch the token's algorithm? AnRS256token needs an RSA key; anES256token needs an EC key. A mismatch means you are holding the wrong key set entirely. - Is
useset tosig? A key published for encryption will not verify signatures, and a key set may legitimately contain both. - Is the curve right? An EC key on P-384 cannot verify an ES256 signature, which is defined over P-256.
- Once the key looks right, confirm the signature itself with our JWT signature verifier, which takes this key set directly and selects the key by
kid.
Caching a Key Set Sensibly
Fetching the JWKS on every request is wasteful and makes your service depend on the issuer's availability for every single call. Fetching it once at startup is worse, because the next key rotation breaks you until a redeploy. The workable pattern is to cache the key set with a modest time-to-live, and additionally re-fetch when a token arrives bearing an unknown kid — with a rate limit on that refresh, so a stream of malformed tokens cannot turn into a fetch storm against the issuer.
Frequently Asked Questions
keys array holding one or more JWKs, which is what an identity provider publishes at its jwks.json endpoint. This decoder accepts either.kty field — and it is decoded exactly like a one-key set: kid, type, curve or modulus size, RFC 7638 thumbprint, and a PEM public key. A private JWK is accepted too; only its public half is converted.