Logo

Online Tools

JWS Verify Signature

Verify a compact JWS or JWT signature in your browser with a shared secret, PEM public key, JWK, or JWK Set, then inspect the decoded header, payload, and claims. Read more

Verify a JWS signature online

A compact JWS — the same three-segment header.payload.signature form used by most JWTs — proves that whoever holds the signing key produced those exact bytes. This page runs the verification in your browser: paste the token, choose the algorithm you expect, supply the key, and the result shows Valid or Invalid together with the decoded header, payload, and registered claims.

Nothing is uploaded — the whole verification runs in your browser. Remember Input and Share are available like everywhere else on this site: Remember Input keeps the token, key, and settings in your browser’s local storage, and Share puts them in the link. Both are opt-in and neither sends anything to a server, but a shared link does carry whatever is in the fields, so keep test keys and test tokens in here rather than production ones.

How to verify

  1. Paste the compact JWS. A leading Bearer prefix from an Authorization header is accepted and stripped.
  2. Leave Algorithm on Auto (from header), or pick a specific algorithm to pin it. With a pinned algorithm, a token whose header names a different one is reported as a mismatch instead of being verified.
  3. Choose the key type and paste the key:
    • UTF-8, Hex, or Base64 — the shared secret for HS256, HS384, or HS512, in whichever spelling you have it. A passphrase such as s3cr3t is UTF-8. Base64 accepts both the standard and the URL-safe alphabet, with or without = padding, so the k value copied straight out of a JWK works here too.
    • Pem Text — an RSA or EC public key for RS*, PS*, or ES*. A private key is accepted too; the public part is taken out of it.
    • JWK (Set) — either a single JSON Web Key or a whole jwks.json; the two shapes tell themselves apart, so paste whichever you have. "kty": "oct" works for HMAC, "RSA" and "EC" for the asymmetric algorithms, and a private JWK (one carrying d) is accepted too. For a set: if the header names a kid, only that key is used; otherwise the set is narrowed by use, alg, and kty and every remaining key is tried in order, and the result names the one that matched.
  4. Verify, and read the Header, Payload, and Registered Claims sections below the result. A payload that is not a JSON claims set is shown as plain decoded text — that is ordinary JWS, not an error.

Auto, and why your own code should not do this

alg lives in the header, which is part of the untrusted token. A service that simply does whatever the header says can be attacked: swapping RS256 for HS256 invites the verifier to reuse the public key as an HMAC secret, and "alg": "none" removes the signature entirely.

Auto is safe here because the key never changes meaning behind your back. You pick the key type yourself, so a PEM public key can only ever be used as a public key — a token that switches to HS256 gets HS256 needs a shared secret, not a forged pass. "alg": "none" is rejected outright in either mode.

Your own verifier has no such human in the loop: it picks the key automatically, which is exactly the gap the attack walks through. Pin the accepted algorithms in its configuration and never let the token choose. Pin the algorithm here too when that is what you are testing — a token that should be RS256 and arrives as something else then shows up as a mismatch.

What a valid signature does and does not mean

A valid result means the signature matches this exact token and this exact key. It does not tell you that the key belongs to who you think it does, that the token is still within its validity window, or that the claims inside are authorized for the operation being attempted. Fetch keys over an authenticated channel, then check iss, aud, exp, and nbf against your own policy. The Registered Claims table shows the timestamps and flags an expired exp or a future nbf, but those are observations — they are not part of the signature check.

Supported algorithms

Family Algorithms Key
HMAC HS256, HS384, HS512 Shared secret, or a JWK with "kty": "oct"
RSASSA-PKCS1-v1_5 RS256, RS384, RS512 RSA public key (PEM or JWK)
RSASSA-PSS PS256, PS384, PS512 RSA public key (PEM or JWK)
ECDSA ES256, ES384, ES512 EC public key on P-256, P-384, P-521 respectively

Each ECDSA algorithm is defined for exactly one curve, so ES256 needs P-256, ES384 needs P-384, and ES512 needs P-521. A key on any other curve is rejected here even though some libraries will quietly sign and verify such a pair — a token built that way is not one other implementations will accept.

EdDSA (Ed25519) is not supported yet. Out of scope on this page: JWE (five-segment encrypted tokens), the JSON serializations, detached payloads, and unencoded payloads ("b64": false, RFC 7797). A crit header is displayed as a warning but its extensions are not enforced.

JWS verification FAQ

Why does my RS256 token fail with a PEM key?

The key has to be the one that pairs with the signing private key. -----BEGIN PUBLIC KEY----- (SPKI) and -----BEGIN RSA PUBLIC KEY----- both parse, and so does a private key — see below. A certificate does not.

Can I verify with a private key?

Yes. A signature is checked with the public key, and a private key contains it, so pasting the private PEM or the private JWK works: the public part is taken out of it and the Key row says so. Keeping a private key out of a window you did not have to open is still the better habit — the public half is all this page needs.

Can I verify with the JWKS URL from my provider?

Not directly — this page never makes network requests. Fetch jwks.json yourself and paste its contents into the Key field with Key Type set to JWK (Set).

Do I need to know whether my secret is Base64 or Base64URL?

No. The Base64 type reads both. Standard Base64 (RFC 4648 §4) uses + and / and pads with =; Base64URL (§5) uses - and _ and usually drops the padding so the value survives being put in a URL. JOSE itself uses Base64URL throughout — the token segments and every JWK value, including the k of an oct key — but either spelling decodes to the same bytes here, padded or not.

My payload is not JSON. Is that a problem?

No. JWS signs arbitrary bytes, and the signature covers the encoded segments either way, so verification is unaffected. Such a payload is displayed as decoded text and the Registered Claims table is left out, because there are no claims to list. Only a JWT is required to carry a JSON claims set.

How does a JWK Set pick the key, and what if my token has no kid?

A kid in the header is an instruction, so it is followed exactly: only the key with that kid is tried, and a set without it fails rather than falling back to something else. Without a kid there is nothing to pick by, so the set is narrowed to the keys that could plausibly have signed this token — use must be sig when present, alg must match when present, and kty must suit the algorithm — and each survivor is tried until one verifies. The Key row then reports which one matched, for example EC / P-256 / kid=key-b (matched candidate 2 of 3).

Trying several keys costs nothing here: a signature only ever verifies against the key that produced it, everything runs locally, and a JWK Set holds public keys to begin with.

The signature is valid but the token is expired. What does the result say?

Valid. The signature check and the time check are separate; expiration appears as a note in the Registered Claims table.

Is my secret or private key sent anywhere?

Not to a server — verification is entirely local. It can still end up somewhere you did not intend: Remember Input writes the fields to your browser’s local storage, and Share encodes them into a link you might paste into a chat. Use throwaway keys, or leave both switched off.

Input
Advertisement
Output
Share Link
Settings
Key
Advertisement