jwk-simple
A Rust library for working with JSON Web Keys (JWK) and JWK Sets (JWKS) as defined in RFC 7517, with WASM compatibility and optional jwt-simple integration.
Features
- RFC coverage (JOSE/JWK): Supports RFC 7517 (JWK), RFC 7518 (algorithms), RFC 8037 (OKP), RFC 9864 (Ed25519/Ed448 JOSE algorithms), and RFC 7638 (thumbprints)
- Multiple key types: RSA, EC (P-256, P-384, P-521, secp256k1), Symmetric (HMAC), and OKP (Ed25519, Ed448, X25519, X448)
- WASM and WebCrypto compatible: Core functionality works in WebAssembly environments, with optional WebCrypto integration on
wasm32 - Security-first: Zeroize support for sensitive data, constant-time base64 encoding
- jwt-simple integration: Optional feature for converting JWKs to
jwt-simplekey types - Remote fetching: Load JWKS from HTTP endpoints with caching support
- Caching: Optional TTL-based caching of fetched JWKS (
KeySet) data
Quick Start
Add to your Cargo.toml:
[]
= "0.1"
= "1"
Parse a JWKS and strictly select a verification key:
use ;
let json = r#"{
"keys": [{
"kty": "RSA",
"kid": "my-key-id",
"use": "sig",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e": "AQAB"
}]
}"#;
let jwks: KeySet = from_str?;
let key = jwks
.selector
.select?;
assert!;
Note: JWKS parsing is permissive and may skip invalid keys.
Feature Flags
Feature definitions live in Cargo.toml ([features]). This table documents
expected usage and platform constraints.
| Feature | Default | Description |
|---|---|---|
jwt-simple |
❌ | Integration with the jwt-simple crate (all targets; requires a jwt-simple backend feature such as jwt-simple/pure-rust) |
http |
❌ | Async HTTP fetching (reqwest, all targets) |
web-crypto |
❌ | WebCrypto API integration (wasm32 only) |
cloudflare |
❌ | Cloudflare Workers support (KV cache + fetch, wasm32 only) |
moka |
❌ | In-memory TTL-based key caching (non-wasm32 only) |
Invalid platform/feature combinations intentionally fail at compile time with
clear compile_error! messages.
Usage Examples
Note: snippets below are focused examples and may omit surrounding async/runtime scaffolding and input setup.
Basic JWKS Parsing
use ;
// Parse from JSON string
let jwks: KeySet = from_str?;
// Find keys by various criteria
let key = jwks.get_by_kid;
let rsa_keys = jwks.find;
let signing_keys = jwks.find;
// Get the first signing key (common pattern)
let first_signing = jwks.first_signing_key;
// Iterate over all keys
for key in &jwks
Converting to jwt-simple Keys
With the jwt-simple feature enabled:
Note: jwt-simple is not re-exported. Add it to your dependencies when using its key types directly, and enable a backend feature (for example pure-rust):
[]
= { = "0.1", = ["jwt-simple"] }
= { = "0.12", = false, = ["pure-rust"] }
use ;
use *;
let jwks: KeySet = from_str?;
let jwk = jwks
.selector
.select?;
// Convert to jwt-simple key type using TryFrom/TryInto
let key: RS256PublicKey = jwk.try_into?;
// Verify a JWT
let claims = key.?;
Fetching from HTTP
With the http feature enabled:
use ;
use Duration;
// Create remote key store.
// Native targets use a default 30s timeout.
// On wasm32, reqwest uses the browser/Fetch backend where
// client-level timeout configuration is not available.
let remote = new?;
// For custom timeout, use a custom client.
// Note: reqwest is not re-exported, so add it to your own Cargo.toml:
// reqwest = "0.13"
let client = builder
.timeout
.build?;
let remote = new_with_client?;
// Fetch the JWKS
let jwks = remote.get_keyset.await?;
Caching Keys
With the http and moka features enabled:
use ;
use Duration;
// For production, wrap a remote store with caching (5 minute TTL)
let cache = new;
let cached = new;
// Keys are automatically cached on first access
let key = cached.get_key.await?;
// Invalidate the entire cache when needed
cached.cache.clear.await?;
JWK Thumbprints (RFC 7638)
use KeySet;
let jwks: KeySet = from_str?;
let key = jwks.first.unwrap;
// Calculate thumbprint (base64url-encoded SHA-256)
let thumbprint = key.thumbprint;
// Find key by thumbprint
let key = jwks.get_by_thumbprint;
Supported Key Types
RSA (kty: "RSA")
- Public keys: n, e
- Private keys: n, e, d, p, q, dp, dq, qi
- Algorithms: RS256, RS384, RS512, PS256, PS384, PS512
Elliptic Curve (kty: "EC")
- Curves: P-256, P-384, P-521, secp256k1
- Public keys: crv, x, y
- Private keys: crv, x, y, d
- Algorithms: ES256, ES384, ES512, ES256K
Symmetric (kty: "oct")
- Keys: k
- Algorithms: HS256, HS384, HS512, A128KW, A192KW, A256KW
Octet Key Pair (kty: "OKP")
- Curves: Ed25519, Ed448, X25519, X448
- Public keys: crv, x
- Private keys: crv, x, d
- Algorithms: Ed25519, Ed448 (preferred), EdDSA (legacy/deprecated)
Comparison to Other Libraries
Status below is based on current crates.io releases as of 2026-03.
| Feature | jwk-simple | jwks-client | jsonwebkey | jwt-simple | jsonwebtoken |
|---|---|---|---|---|---|
| Full JWKS spec | ✅ | ❌ | ❌ | ❌ | ❌ |
| RSA keys | ✅ | ✅ | ✅ | ✅ | ✅ |
| EC keys (P-256/384/521) | ✅ | ❌ | ⚠️ | ⚠️ | ⚠️ |
| EdDSA (Ed25519) | ✅ | ❌ | ❌ | ✅ | ✅ |
| Symmetric keys | ✅ | ❌ | ✅ | ✅ | ✅ |
| OKP keys (X25519) | ✅ | ❌ | ❌ | ❌ | ❌ |
| WASM support | ✅ | ❌ | ⚠️ | ✅ | ✅ |
| jwt-simple integration | ✅ | ❌ | ❌ | N/A | ❌ |
| HTTP fetching | ✅ | ✅ | ❌ | ❌ | ❌ |
| Caching | ✅ | ✅ | ❌ | ❌ | ❌ |
| Zeroize support | ✅ | ❌ | ✅ | ✅ | ❌ |
| Panic-free APIs* | ✅ | ✅ | ❌ | ❌ | ❌ |
| JWK thumbprint (RFC 7638) | ✅ | ❌ | ⚠️ | ❌ | ✅ |
| TryFrom/TryInto traits | ✅ | ❌ | ❌ | ❌ | ⚠️ |
| Private key support | ✅ | ❌ | ✅ | ✅ | ✅ |
Legend for partial support (⚠️):
jsonwebkeyEC = P-256 only; thumbprints require thethumbprintfeaturejwt-simpleEC = P-256/P-384/secp256k1 (not P-521)jsonwebtokenEC = P-256/P-384 (P-521 JWK exists but is not supported by the crypto backend)jsonwebkeyWASM = core parsing works; behavior can depend on enabled optional featuresjsonwebtokenWASM = works onwasm32withrust_crypto(or a customCryptoProvider)jsonwebtokenTryFrom/TryInto =TryFrom<&Jwk>is available forDecodingKey
*"Panic-free APIs" excludes standard panic-prone trait semantics such as out-of-bounds indexing via Index.
When to use jwk-simple
- You need jwt-simple integration - Direct conversion to jwt-simple key types
- You're building Cloudflare Workers - Native KV cache support
- You need WASM support - Core parsing works in browser
- You want broad JOSE/JWK RFC coverage - All key types including OKP
When to use alternatives
- jwks-client - If you only need RSA and want mature async HTTP
- jsonwebkey - If you need key generation and PEM/DER conversion
- jwt-simple - If you only need JWT operations, not JWKS
- jsonwebtoken - If you want the most widely-used JWT library
Security Considerations
This crate prioritizes security:
- Zeroize: Private key parameters are zeroed from memory on drop via the
zeroizecrate - Constant-time base64: Base64 encoding uses constant-time operations via
base64ct - Debug redaction: Debug output redacts sensitive key material
- Panic-free APIs: All fallible operations return
Resulttypes — standard trait implementations likeIndexfollow normal Rust semantics and may panic on invalid input (e.g., out-of-bounds indexing) - Input validation: Key parameters are validated for correct sizes
- Trust boundary clarity:
Key::validateperforms structural and metadata consistency checks only, whileKey::validate_for_useadds algorithm suitability and operation intent checks; PKIX trust validation (chain path, trust anchors, validity, EKU/KU, revocation) must be provided by your application
When enabling the optional jwt-simple integration, note that some jwt-simple
dependency chains may pull in rsa versions affected by
RUSTSEC-2023-0071
(Marvin timing side-channel). This primarily concerns RSA private-key
operations (for example, signing) in attacker-observable timing contexts. If
this matters for your deployment, prefer EdDSA/ECDSA or avoid RSA private-key
operations until an upstream patched chain is available.
WASM Usage
Core JWKS parsing works in WebAssembly environments.
httpis available onwasm32via reqwest's Fetch backendweb-cryptoandcloudflarearewasm32-only featuresmokais not available onwasm32
Example for WASM:
use KeySet;
// In WASM, fetch JWKS via browser APIs, then parse
let jwks: KeySet = from_str?;
let key = jwks.get_by_kid.expect;
License
The project is licensed under the MIT License.