0
# JSON Web Keys (JWK)
1
2
JSON Web Key handling and cryptographic key abstraction supporting RSA, ECDSA, EdDSA, and symmetric keys. Provides key parsing, validation, algorithm detection, and integration with JWT/JWS operations for secure key management.
3
4
## Capabilities
5
6
### PyJWK - Individual Key Management
7
8
Represents a single JSON Web Key with automatic algorithm detection and cryptographic key parsing.
9
10
```python { .api }
11
class PyJWK:
12
def __init__(self, jwk_data: dict, algorithm: str = None):
13
"""
14
Initialize PyJWK from JWK dictionary.
15
16
Args:
17
jwk_data (dict): JWK data according to RFC 7517
18
algorithm (str): Force specific algorithm (optional)
19
20
Raises:
21
InvalidKeyError: Invalid key format or unsupported key type
22
PyJWKError: General JWK processing error
23
MissingCryptographyError: Cryptography library required
24
"""
25
26
@staticmethod
27
def from_dict(obj: dict, algorithm: str = None) -> 'PyJWK':
28
"""
29
Create PyJWK from dictionary.
30
31
Args:
32
obj (dict): JWK dictionary
33
algorithm (str): Optional algorithm override
34
35
Returns:
36
PyJWK: Initialized key object
37
"""
38
39
@staticmethod
40
def from_json(data: str, algorithm: str = None) -> 'PyJWK':
41
"""
42
Create PyJWK from JSON string.
43
44
Args:
45
data (str): JWK JSON string
46
algorithm (str): Optional algorithm override
47
48
Returns:
49
PyJWK: Initialized key object
50
"""
51
52
@property
53
def key_type(self) -> str:
54
"""Key type (kty parameter): 'RSA', 'EC', 'oct', 'OKP'."""
55
56
@property
57
def key_id(self) -> str:
58
"""Key ID (kid parameter) for key identification."""
59
60
@property
61
def public_key_use(self) -> str:
62
"""Public key use (use parameter): 'sig', 'enc', or None."""
63
64
@property
65
def algorithm_name(self) -> str:
66
"""Detected or assigned algorithm name."""
67
68
@property
69
def Algorithm(self):
70
"""Algorithm implementation object."""
71
72
@property
73
def key(self):
74
"""Underlying cryptographic key object."""
75
```
76
77
Usage examples:
78
79
```python
80
import jwt
81
from jwt import PyJWK
82
83
# RSA public key from JWK
84
rsa_jwk_data = {
85
"kty": "RSA",
86
"kid": "rsa-key-1",
87
"use": "sig",
88
"n": "base64url-encoded-modulus",
89
"e": "AQAB"
90
}
91
92
jwk = PyJWK.from_dict(rsa_jwk_data)
93
print(f"Key type: {jwk.key_type}")
94
print(f"Key ID: {jwk.key_id}")
95
print(f"Algorithm: {jwk.algorithm_name}")
96
97
# Use with JWT decoding
98
token = "eyJ..."
99
payload = jwt.decode(token, jwk.key, algorithms=[jwk.algorithm_name])
100
101
# ECDSA key with explicit algorithm
102
ec_jwk_data = {
103
"kty": "EC",
104
"kid": "ec-key-1",
105
"crv": "P-256",
106
"x": "base64url-encoded-x",
107
"y": "base64url-encoded-y"
108
}
109
110
jwk = PyJWK.from_dict(ec_jwk_data, algorithm="ES256")
111
112
# Symmetric key (HMAC)
113
oct_jwk_data = {
114
"kty": "oct",
115
"kid": "hmac-key-1",
116
"k": "base64url-encoded-secret"
117
}
118
119
jwk = PyJWK.from_dict(oct_jwk_data) # Defaults to HS256
120
```
121
122
### PyJWKSet - Key Set Management
123
124
Manages collections of JSON Web Keys with key lookup capabilities and validation.
125
126
```python { .api }
127
class PyJWKSet:
128
def __init__(self, keys: list):
129
"""
130
Initialize JWK Set from list of JWK dictionaries.
131
132
Args:
133
keys (list): List of JWK dictionaries
134
135
Raises:
136
PyJWKSetError: Invalid JWK Set or no usable keys
137
MissingCryptographyError: Required for asymmetric keys
138
"""
139
140
@staticmethod
141
def from_dict(obj: dict) -> 'PyJWKSet':
142
"""
143
Create PyJWKSet from JWKS dictionary.
144
145
Args:
146
obj (dict): JWKS dictionary with 'keys' array
147
148
Returns:
149
PyJWKSet: Initialized key set
150
"""
151
152
@staticmethod
153
def from_json(data: str) -> 'PyJWKSet':
154
"""
155
Create PyJWKSet from JWKS JSON string.
156
157
Args:
158
data (str): JWKS JSON string
159
160
Returns:
161
PyJWKSet: Initialized key set
162
"""
163
164
def __getitem__(self, kid: str) -> PyJWK:
165
"""
166
Get key by key ID.
167
168
Args:
169
kid (str): Key ID to look up
170
171
Returns:
172
PyJWK: Matching key
173
174
Raises:
175
KeyError: No key found with given ID
176
"""
177
178
@property
179
def keys(self) -> list:
180
"""List of PyJWK objects in this set."""
181
```
182
183
Usage examples:
184
185
```python
186
import jwt
187
from jwt import PyJWKSet
188
189
# JWKS from dictionary
190
jwks_data = {
191
"keys": [
192
{
193
"kty": "RSA",
194
"kid": "rsa-key-1",
195
"use": "sig",
196
"n": "modulus...",
197
"e": "AQAB"
198
},
199
{
200
"kty": "EC",
201
"kid": "ec-key-1",
202
"crv": "P-256",
203
"x": "x-coordinate...",
204
"y": "y-coordinate..."
205
}
206
]
207
}
208
209
jwk_set = PyJWKSet.from_dict(jwks_data)
210
211
# Get specific key by ID
212
key = jwk_set['rsa-key-1']
213
print(f"Found key: {key.algorithm_name}")
214
215
# Iterate through all keys
216
for jwk in jwk_set.keys:
217
print(f"Key {jwk.key_id}: {jwk.algorithm_name}")
218
219
# From JSON string
220
jwks_json = '''{"keys": [...]}'''
221
jwk_set = PyJWKSet.from_json(jwks_json)
222
223
# Use with JWT verification
224
def verify_jwt_with_jwks(token, jwk_set):
225
# Extract key ID from token header
226
unverified_header = jwt.get_unverified_header(token)
227
kid = unverified_header.get('kid')
228
229
if not kid:
230
raise ValueError("Token missing key ID")
231
232
# Get the appropriate key
233
key = jwk_set[kid]
234
235
# Verify token
236
return jwt.decode(token, key.key, algorithms=[key.algorithm_name])
237
```
238
239
### Key Type Support
240
241
PyJWK supports all standard JSON Web Key types:
242
243
#### RSA Keys (kty: "RSA")
244
245
```python
246
# RSA public key
247
rsa_public_jwk = {
248
"kty": "RSA",
249
"kid": "rsa-pub-1",
250
"use": "sig",
251
"n": "modulus-base64url", # Required
252
"e": "exponent-base64url" # Required
253
}
254
255
# RSA private key (includes all public parameters plus private)
256
rsa_private_jwk = {
257
"kty": "RSA",
258
"kid": "rsa-priv-1",
259
"use": "sig",
260
"n": "modulus-base64url",
261
"e": "exponent-base64url",
262
"d": "private-exponent-base64url", # Private key
263
# Optional CRT parameters: p, q, dp, dq, qi
264
}
265
```
266
267
#### Elliptic Curve Keys (kty: "EC")
268
269
```python
270
# ECDSA P-256 public key
271
ec_p256_jwk = {
272
"kty": "EC",
273
"kid": "ec-p256-1",
274
"crv": "P-256", # Curve: P-256, P-384, P-521, secp256k1
275
"x": "x-coordinate-base64url",
276
"y": "y-coordinate-base64url"
277
}
278
279
# ECDSA private key (includes 'd' parameter)
280
ec_private_jwk = {
281
"kty": "EC",
282
"kid": "ec-priv-1",
283
"crv": "P-256",
284
"x": "x-coordinate-base64url",
285
"y": "y-coordinate-base64url",
286
"d": "private-key-base64url" # Private key
287
}
288
```
289
290
#### Symmetric Keys (kty: "oct")
291
292
```python
293
# HMAC symmetric key
294
oct_jwk = {
295
"kty": "oct",
296
"kid": "hmac-1",
297
"k": "symmetric-key-base64url" # The symmetric key material
298
}
299
```
300
301
#### EdDSA Keys (kty: "OKP")
302
303
```python
304
# Ed25519 public key
305
ed25519_jwk = {
306
"kty": "OKP",
307
"kid": "ed25519-1",
308
"crv": "Ed25519", # Or "Ed448"
309
"x": "public-key-base64url"
310
}
311
312
# Ed25519 private key
313
ed25519_private_jwk = {
314
"kty": "OKP",
315
"kid": "ed25519-priv-1",
316
"crv": "Ed25519",
317
"x": "public-key-base64url",
318
"d": "private-key-base64url" # Private key
319
}
320
```
321
322
### Algorithm Detection
323
324
PyJWK automatically detects appropriate algorithms based on key type and curve:
325
326
| Key Type | Curve/Size | Default Algorithm |
327
|----------|------------|-------------------|
328
| RSA | Any | RS256 |
329
| EC | P-256 | ES256 |
330
| EC | P-384 | ES384 |
331
| EC | P-521 | ES512 |
332
| EC | secp256k1 | ES256K |
333
| oct | Any | HS256 |
334
| OKP | Ed25519 | EdDSA |
335
| OKP | Ed448 | EdDSA |
336
337
```python
338
# Algorithm detection examples
339
rsa_jwk = PyJWK.from_dict({"kty": "RSA", "n": "...", "e": "AQAB"})
340
print(rsa_jwk.algorithm_name) # "RS256"
341
342
ec_jwk = PyJWK.from_dict({"kty": "EC", "crv": "P-384", "x": "...", "y": "..."})
343
print(ec_jwk.algorithm_name) # "ES384"
344
345
# Override automatic detection
346
forced_jwk = PyJWK.from_dict(rsa_jwk_data, algorithm="PS256")
347
print(forced_jwk.algorithm_name) # "PS256"
348
```
349
350
### Error Handling
351
352
Common JWK-related exceptions:
353
354
```python
355
import jwt
356
from jwt.exceptions import PyJWKError, InvalidKeyError, MissingCryptographyError
357
358
try:
359
jwk = PyJWK.from_dict(invalid_jwk_data)
360
except InvalidKeyError as e:
361
print(f"Invalid key format: {e}")
362
except MissingCryptographyError as e:
363
print(f"Install cryptography: {e}")
364
except PyJWKError as e:
365
print(f"JWK error: {e}")
366
367
try:
368
key = jwk_set['nonexistent-kid']
369
except KeyError as e:
370
print(f"Key not found: {e}")
371
```