JWT Signer & Verifier
Sign a token with your own key, verify one against a secret, PEM, JWK or live JWKS — and build the key set your API publishes. All crypto runs in your browser.
A JWT is only as trustworthy as its signature. Anyone can read a token — decoding proves nothing — so this tool does the part a decoder cannot: it signs tokens with your key (HS256/384/512, RS256/384/512, ES256/384/512, EdDSA), verifies them against a secret, a PEM or JWK public key, a pasted JWKS or a live JWKS URL with kid matching, and builds the JWKS your own API publishes. The companion guide signing and verifying JWTs explains why decoding is not verifying, how the algorithm-confusion attack works, and what "verified" does — and does not — prove. To read a token without a key, use the JWT Decoder; for the keys themselves, the SSH Key Suite covers generation and conversion.
Last updated:
Sign a token
That secret is under 32 characters. Short secrets fall to offline guessing fast — use a long random secret for anything real.
Verify a token
“Verified” means the signature matches this key and the token is inside its validity window. It does not mean the issuer is trusted — that depends on where the key came from.
Build & validate a JWKS
Build a JWKS
Add keys from a generated key, a PEM or a pasted JWK. Each entry gets a kid — an RFC 7638 thumbprint where computable, otherwise a random id the page labels as such.
Keys in this set
No keys yet. Generate one or paste a PEM or JWK above.
Validate a JWKS
Convert a key (PEM ⇄ JWK)
Paste a key on the Sign tab, generate one there, or convert below. HMAC secrets have no PEM form — an oct JWK is the whole key.
🔒 Signing and verification happen locally with WebCrypto. Secrets and private keys are never sent, stored or logged — refresh the page and they are gone.
Your keys never leave this page
Signing and verification use your browser's WebCrypto, on keys you paste or generate here. Secrets and private keys live in page memory only: they are not uploaded, not written to localStorage or cookies, and never placed in the URL. Refresh or close the page and they are gone. The single exception is a JWKS URL fetch, which you start yourself: it sends only the URL you typed, to fetch public keys — never a secret. Read signing and verifying JWTs before pasting a production key anywhere.
The honest limits
EdDSA needs a current browser: WebCrypto Ed25519 is not in older browsers, and this page says so plainly instead of faking a signature. ECDSA P-256 is offered as the fallback that works everywhere.
Encrypted PEM (a private key locked with a passphrase) is not supported in version 1. The page refuses it openly — decrypt it with openssl pkcs8 -topk8 -nocrypt first, and never paste a passphrase into a web page.
A JWKS URL fetch depends on that server sending CORS headers. Many identity providers do; when one does not, the fetch fails honestly and the page tells you to paste the JWKS instead. Only public keys are ever fetched — a JWKS URL never carries a secret.
When signing beats decoding
Decoding a JWT is reading it: the header and payload are base64url, and anyone holding the token can see both. That is why a decoder alone can never answer the only question that matters — did the issuer really sign this? Signing is the other half of the job: whoever holds the private key (or the HMAC secret) produces the signature, and verification replays the same maths with the public key. This page keeps both halves in your browser, because the keys involved are the most sensitive inputs a web tool ever handles.
The split matters most for asymmetric algorithms. With HS256, the secret that verifies is the same secret that signs — anyone who can check your tokens can also mint them, which is fine inside one service and a leak everywhere else. With RS256, ES256 or EdDSA, the signing key stays with the issuer and only the public key travels: your API can publish its JWKS, rotate keys by adding a new kid, and let every verifier in the world check tokens without ever holding the power to forge one.
What the tool refuses to do
Two refusals are deliberate. The first is alg: none: a token that claims to need no signature is rejected outright, with the reason shown — a verifier that accepts it accepts forgeries. The second is algorithm confusion: an RS256 token offered with an HMAC secret (or the reverse) fails with an explicit warning, never by quietly reinterpreting the key in the other family. Both are classic JWT attacks, and both are guarded structurally here — the key family must match the token's alg family, or verification fails.
The third refusal is subtler: this page never tells you a token is "trusted". A valid signature proves the token was signed with a key, and the expiry check proves it is inside its window — nothing more. Whether to trust that key is a decision about where it came from: a JWKS fetched from your identity provider's well-known URL, a public key you pinned yourself, a secret you generated. The guide signing and verifying JWTs walks through what "verified" does and does not prove.
JWT terms, pinned down
JWT. JSON Web Token (RFC 7519): a compact, URL-safe way to carry claims between parties — header.payload.signature, each base64url-encoded. The payload is readable by anyone; only the signature is secret-dependent.
JWS. JSON Web Signature (RFC 7515): the signing format underneath JWTs. A JWT is a JWS whose payload is a JSON claims set.
JWK. JSON Web Key (RFC 7517): a JSON object representing one cryptographic key — kty, and the key material (n/e for RSA, x/y for EC, x for Ed25519, k for a shared secret).
JWKS. JSON Web Key Set: an object with a "keys" array — the standard way an issuer publishes its public keys, usually at /.well-known/jwks.json.
kid. Key ID: the header claim that says which key signed a token. A verifier matches it against the JWKS; without it, a set with one key is used and the page says so.
alg. The header claim naming the signing algorithm (HS256, RS256, ES256, EdDSA…). A verifier must pin the algorithms it accepts — trusting the token's own alg claim is the confusion attack.
HMAC. Hash-based Message Authentication Code: a symmetric signature — the same secret signs and verifies. Fast and simple; everyone holding the secret can mint tokens.
RSASSA-PKCS1-v1_5. The RSA signature scheme behind RS256/384/512: the hash is wrapped and raised to the private exponent. Deterministic — the same input always yields the same signature bytes.
EdDSA. Edwards-curve Digital Signature Algorithm: Ed25519 here — small keys, fast verification, deterministic signatures. Needs a current browser for WebCrypto support.
Frequently asked questions
Is it safe to paste a private key into this page?
On this page the key never leaves your browser: signing runs in page memory with WebCrypto, nothing is uploaded, stored or logged, and a refresh wipes it. The unsafe pattern is a server-side signer, where your private key crosses the network. Even locally, use a device you trust — and treat a pasted production key as exposed if the machine is shared. Generate test keys here instead of pasting real ones.
Why will my RS256 token not verify with my secret?
Because a secret and an RSA key are different families, and the page refuses to mix them — that refusal is the algorithm-confusion guard. An RS256 token needs the issuer's RSA public key (PEM, JWK or JWKS); a secret can only verify HS256/384/512. If you hold the public key in the wrong form, convert it: the JWKS tab converts PEM ⇄ JWK.
What does a JWKS URL fetch actually send?
One GET request to the URL you typed, asking for JSON. No key, no secret, no token and no identifier goes with it — the response is a set of public keys. If the server does not send CORS headers, the browser blocks the read and the page tells you to paste the JWKS instead.
My token verifies here but the API rejects it. Why?
Verification here checks the signature and the time window against the key you supplied. An API also checks the claims: the issuer (iss) it expects, the audience (aud) it serves, and whether the key id (kid) is one it currently trusts. A token signed with a rotated-out key, or minted for a different audience, verifies mathematically and still gets refused — correctly.
EdDSA does not work in my browser. What now?
Ed25519 needs a current browser — older ones lack WebCrypto Ed25519 entirely, and this page says so instead of faking it. Use ES256 (ECDSA P-256) as the fallback: it is supported everywhere WebCrypto exists, and the page can generate the key for you. If you must use Ed25519, update the browser.