JWK Generator
Generate a key and get a proper JWK — alg, use and an RFC 7638 kid already stamped, with the JWKS wrapper you actually deploy.
Public half — safe to publish
Public JWK
Nothing generated yet.
JWKS (public keys)
Nothing generated yet.
Public key — SPKI PEM
Nothing generated yet.
Private half — keep this secret
Private JWK
Nothing generated yet.
Private key — PKCS#8 PEM
Nothing generated yet.
Members are emitted in a fixed order per key type, with alg, use and kid last, so the same key always reads the same way.
What this tool does not do
- No certificates and no CSRs.
- No SSH key formats.
- No passphrase-encrypted private keys.
- No encryption keys — nothing here is stamped use: enc.
Press ? for shortcuts · Nothing is kept between visits: no key, no option, no reveal state. Reloading loses the key.
Keys are generated here in your browser with Web Crypto. Nothing is uploaded.
Do it offline:$ openssl genrsa 2048
How to use it
- Choose the family: RSA for compatibility, EC for short signatures, Ed25519 for the shortest keys, or oct for a shared HMAC secret.
- Choose the size — RSA 2048, 3072 or 4096; P-256, P-384 or P-521; Ed25519; oct 256, 384 or 512 — then the algorithm stamped on the key. RSA rows offer both RS and PS on one key.
- Decide how kid is set: the RFC 7638 thumbprint, a kid you type, or none. A typed kid is capped at 128 characters, and control characters are refused rather than stripped.
- Press Generate, or ⌘/Ctrl + Enter. RSA 4096 takes measurably longer, so the cards hold their size and show elapsed milliseconds instead of a progress bar WebCrypto cannot supply.
- Copy or download the public JWK, the JWKS wrapper for your discovery endpoint, or the private half, which stays masked until you reveal it.
Pick a family and a size, press Generate, and take away a JSON Web Key that is ready to publish: alg and use stamped, an RFC 7638 thumbprint as kid, members in a fixed order, and the JWKS wrapper built for you. Both PEM forms come along for the asymmetric families. Keys are generated by WebCrypto on this device, shown once, and never stored or transmitted.
How it works
A thumbprint is not a hash of the JWK you are looking at. RFC 7638 §3.1 fixes a subset of members per key type — e, kty and n for RSA; crv, kty, x and y for an elliptic-curve key; crv, kty and x for Ed25519; k and kty for a symmetric key — orders them lexicographically, and serializes them as JSON with no whitespace and no other member present. SHA-256 over those bytes, base64url without padding, is the kid you see, so two parties following the rule always agree on a key's name.
The rest is repair work on what WebCrypto returns. An ECDSA export arrives with no alg member, so a consumer cannot tell ES256 from ES384 without reading the curve; alg and use are written in explicitly, for every family. The ext and key_ops members describe the browser's handle rather than the key, so they are dropped, and what remains is rebuilt in a fixed order.
Examples
EC · P-256 · ES256 · kid from thumbprint{
"kty": "EC",
"crv": "P-256",
"x": "YEqdI0duELXhd2D6JUftoLRfFrmErNcobKDcBp0haO0",
"y": "a0Od4vc8BWQibgN56w4pk-2aGUCe4r0wBz8UYEi_NEk",
"alg": "ES256",
"use": "sig",
"kid": "UOGS3FjcFhwHSuA-cdN7TNTPPnTMmvPJP1DxMnz17T4"
}The public half as the page prints it: WebCrypto returned kty, crv, x and y with no alg, and the last three members were added here. Keys are random, so the test pins member order and recomputes the kid rather than the bytes.
OKP · Ed25519 · EdDSA · kid typed as 2026-08-signing{
"kty": "OKP",
"crv": "Ed25519",
"x": "zZRRz9wB7o_hRmzGSuL2mf5Vsx9x0cy5WAlTB7mnLLg",
"alg": "EdDSA",
"use": "sig",
"kid": "2026-08-signing"
}
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAzZRRz9wB7o/hRmzGSuL2mf5Vsx9x0cy5WAlTB7mnLLg=
-----END PUBLIC KEY-----A typed kid is used verbatim, and the SPKI PEM beside it is the same key: the 32 bytes ending that DER structure are the bytes x encodes.
oct · 256 · HS256 · kid from thumbprint (edge case){
"kty": "oct",
"k": "FQfkKS8sBsVeL4AjCN-kHdgGmqB_MRpGIkoeTVipY3I",
"alg": "HS256",
"use": "sig",
"kid": "QEjkhcsrJGPH4fPSpw_fGMK73OaV640VXBP4Oow7z1o"
}A symmetric key has no second half, so the public, JWKS and PEM cards are absent rather than empty, each with its reason printed. This one JWK is the secret, and the sample is burned.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
kid changes every time you generate
A thumbprint is computed from the key, so a new key means a new kid — useful in rotation, and the reason re-generating cannot reproduce a published identifier.
RS and PS are one key, two stamps
PKCS#1 v1.5 and PSS signing use the same RSA key material: picking PS256 over RS256 changes the alg member and nothing else.
Ed25519 needs a recent browser
Ed25519 reached Web Crypto later than the other families: Chrome and Edge 137, Firefox 130, Safari 17. Where it is missing the page says so in place of the key.
P-521 is included, and its signatures are 132 bytes
ES512 signs with P-521, whose raw r and s values run 66 bytes each — tripping consumers that expect a 64-byte ECDSA signature.
The private half is masked, and nothing is remembered
Private JWK and PEM cards need a deliberate reveal that is never remembered, and a shared link carries parameters only.
Common errors
No key was generated: family "rsa" at size "1024" with alg "RS256" is not one of this generator's rows. Accepted rows are RSA 2048 (RS256/RS384/RS512/PS256/PS384/PS512), RSA 3072 (RS256/RS384/RS512/PS256/PS384/PS512), RSA 4096 (RS256/RS384/RS512/PS256/PS384/PS512), P-256 (ES256), P-384 (ES384), P-521 (ES512), Ed25519 (EdDSA), oct 256 (HS256), oct 384 (HS384/HS256), oct 512 (HS512/HS384/HS256).- Reached by hand-editing a shared link, since the form offers only rows that exist. The refusal lists them and substitutes nothing.
No key was generated: custom kid is selected but the kid is empty. Type a kid, or switch to the RFC 7638 thumbprint or to no kid at all.- An empty kid member and no kid member read differently to a consumer, so a blank box is asked about, not guessed at.
No key was generated: the custom kid is 140 characters and the maximum is 128. Nothing was truncated — a shortened key id would not name the key it is attached to. Shorten the kid and generate again.- The cap is 128 characters and the refusal names the length supplied. A kid that no longer matches your token header would be worse than no key at all.
No key was generated: the custom kid contains the control character U+0009 at position 5. A JWK kid is a plain string member (RFC 7517 §4.5) — remove the control character and generate again.- Tabs and newlines arrive by accident when pasting and are invisible in the field, so the position is reported.
No key was generated: WebCrypto is unavailable here (crypto.subtle is missing). Key generation needs a secure context — HTTPS, or localhost during development.- Browsers expose crypto.subtle only in a secure context, so on plain http away from localhost there is no generator to call.
FAQ
Is the private key really generated in my browser?
Yes. The key comes from your browser's WebCrypto implementation and nothing is uploaded: no request carries it anywhere, and no third-party origin is contacted. The test suite reads the engine, the island and the worker and fails on any request call.
Which key should I publish in my JWKS?
The public JWK, which is what the JWKS card contains: kty, the curve or modulus members, alg, use and kid, and no private member. Serve it at your discovery URL. An oct key has no publishable form.
Can I turn a PEM I already have into a JWK here?
No — this page only mints keys. Converting one that already exists lives on the PEM to JWK page. Certificates, certificate signing requests, SSH formats and passphrase-encrypted keys are outside this tool.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Generate a new key with the current selection
- ⌘/Ctrl + Shift + C
- Copy the focused card, revealed or not
- ? or ⌘/Ctrl + /
- Show the keyboard shortcut help