0
# JWK Management
1
2
JSON Web Key functionality for constructing, managing, and converting between different key formats and representations. JWK provides a standardized way to represent cryptographic keys in JSON format for use with JOSE operations.
3
4
## Capabilities
5
6
### Key Construction
7
8
Constructs JWK objects from various key formats and representations for use with JOSE operations.
9
10
```python { .api }
11
def construct(key_data, algorithm=None):
12
"""
13
Constructs a JWK from key data.
14
15
Args:
16
key_data (str or bytes or dict): The key material in various formats:
17
- String secrets for HMAC algorithms
18
- PEM-formatted RSA/EC keys (public or private)
19
- SSH-formatted keys
20
- JWK dictionaries (JSON Web Key format)
21
- Raw key bytes
22
algorithm (str, optional): Algorithm hint for key construction.
23
Helps determine the appropriate key type when ambiguous
24
25
Returns:
26
Key: A key object (HMACKey, RSAKey, ECKey, AESKey, or DIRKey)
27
that can be used with jwt, jws, and jwe operations
28
29
Raises:
30
JWKError: If the key cannot be constructed or format is invalid
31
"""
32
```
33
34
**Usage Examples:**
35
36
```python
37
from jose import jwk
38
from jose.constants import ALGORITHMS
39
40
# HMAC key from string secret
41
key = jwk.construct('my-secret-key', algorithm=ALGORITHMS.HS256)
42
43
# HMAC key from bytes
44
key = jwk.construct(b'binary-secret-key', algorithm=ALGORITHMS.HS256)
45
46
# RSA key from PEM format
47
rsa_pem = """-----BEGIN PRIVATE KEY-----
48
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
49
-----END PRIVATE KEY-----"""
50
51
rsa_key = jwk.construct(rsa_pem, algorithm=ALGORITHMS.RS256)
52
53
# RSA public key
54
rsa_public_pem = """-----BEGIN PUBLIC KEY-----
55
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
56
-----END PUBLIC KEY-----"""
57
58
rsa_public_key = jwk.construct(rsa_public_pem)
59
60
# EC key from PEM format
61
ec_pem = """-----BEGIN PRIVATE KEY-----
62
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
63
-----END PRIVATE KEY-----"""
64
65
ec_key = jwk.construct(ec_pem, algorithm=ALGORITHMS.ES256)
66
67
# JWK dictionary format
68
jwk_dict = {
69
'kty': 'oct', # Key type: octet sequence (symmetric)
70
'k': 'GawgguFyGrWKav7AX4VKUg', # base64url-encoded key value
71
'alg': 'HS256', # Algorithm
72
'use': 'sig' # Usage: signature
73
}
74
key = jwk.construct(jwk_dict)
75
76
# RSA JWK dictionary
77
rsa_jwk = {
78
'kty': 'RSA',
79
'n': 'public-modulus-base64url',
80
'd': 'private-exponent-base64url',
81
'e': 'AQAB', # public exponent (65537)
82
'alg': 'RS256'
83
}
84
rsa_key = jwk.construct(rsa_jwk)
85
86
# EC JWK dictionary
87
ec_jwk = {
88
'kty': 'EC',
89
'crv': 'P-256', # Curve
90
'x': 'x-coordinate-base64url',
91
'y': 'y-coordinate-base64url',
92
'd': 'private-key-base64url',
93
'alg': 'ES256'
94
}
95
ec_key = jwk.construct(ec_jwk)
96
97
# AES key for JWE
98
aes_key_bytes = b'This is a 32-byte key for AES256!!'
99
aes_key = jwk.construct(aes_key_bytes, algorithm=ALGORITHMS.A256GCM)
100
```
101
102
### Key Class Retrieval
103
104
Gets the appropriate key class for a given algorithm, useful for backend selection and key type determination.
105
106
```python { .api }
107
def get_key(algorithm):
108
"""
109
Get key class for algorithm.
110
111
Args:
112
algorithm (str): The algorithm name (e.g., 'HS256', 'RS256', 'ES256')
113
114
Returns:
115
type or None: The key class that handles the algorithm, or None if not found
116
"""
117
```
118
119
**Usage Examples:**
120
121
```python
122
from jose import jwk
123
from jose.constants import ALGORITHMS
124
125
# Get key class for HMAC
126
hmac_key_class = jwk.get_key(ALGORITHMS.HS256)
127
print(hmac_key_class) # <class 'jose.backends.cryptography_backend.CryptographyHMACKey'>
128
129
# Get key class for RSA
130
rsa_key_class = jwk.get_key(ALGORITHMS.RS256)
131
print(rsa_key_class) # <class 'jose.backends.cryptography_backend.CryptographyRSAKey'>
132
133
# Get key class for EC
134
ec_key_class = jwk.get_key(ALGORITHMS.ES256)
135
print(ec_key_class) # <class 'jose.backends.cryptography_backend.CryptographyECKey'>
136
137
# Check if algorithm is supported
138
if jwk.get_key('UNSUPPORTED_ALG') is None:
139
print("Algorithm not supported")
140
141
# Dynamic key construction
142
algorithm = ALGORITHMS.HS256
143
key_class = jwk.get_key(algorithm)
144
if key_class:
145
key = key_class('secret-key', algorithm)
146
```
147
148
### Key Registration
149
150
Registers custom key classes for specific algorithms, enabling extensibility for custom cryptographic backends.
151
152
```python { .api }
153
def register_key(algorithm, key_class):
154
"""
155
Registers a key class for an algorithm.
156
157
Args:
158
algorithm (str): The algorithm name to register
159
key_class (type): The key class that must inherit from jose.backends.base.Key
160
161
Returns:
162
bool: True if registration was successful
163
164
Raises:
165
JWKError: If the key class is invalid or doesn't inherit from Key
166
"""
167
```
168
169
**Usage Examples:**
170
171
```python
172
from jose import jwk
173
from jose.backends.base import Key
174
175
# Custom key implementation
176
class CustomHMACKey(Key):
177
def __init__(self, key_data, algorithm):
178
super().__init__(key_data, algorithm)
179
# Custom initialization
180
181
def sign(self, msg):
182
# Custom signing implementation
183
pass
184
185
def verify(self, msg, sig):
186
# Custom verification implementation
187
pass
188
189
# Register custom key
190
success = jwk.register_key('CUSTOM_HMAC', CustomHMACKey)
191
if success:
192
print("Custom key registered successfully")
193
194
# Use registered key
195
key = jwk.construct('secret', algorithm='CUSTOM_HMAC')
196
```
197
198
## Key Classes
199
200
The JWK module provides access to various key implementation classes through different cryptographic backends.
201
202
### Base Key Class
203
204
```python { .api }
205
class Key:
206
"""
207
Base class for all key implementations.
208
209
Args:
210
prepared_key: The prepared key material
211
algorithm (str): The algorithm this key will be used with
212
"""
213
214
def __init__(self, prepared_key, algorithm):
215
"""Initialize the key with prepared key material and algorithm."""
216
217
def sign(self, msg):
218
"""
219
Sign a message (abstract method).
220
221
Args:
222
msg (bytes): The message to sign
223
224
Returns:
225
bytes: The signature
226
"""
227
228
def verify(self, msg, sig):
229
"""
230
Verify a signature (abstract method).
231
232
Args:
233
msg (bytes): The original message
234
sig (bytes): The signature to verify
235
236
Returns:
237
bool: True if signature is valid
238
"""
239
240
def encrypt(self, plaintext):
241
"""Encrypt plaintext (abstract method for JWE keys)."""
242
243
def decrypt(self, ciphertext, **kwargs):
244
"""Decrypt ciphertext (abstract method for JWE keys)."""
245
246
def wrap_key(self, key_data):
247
"""Wrap a key (abstract method for key wrapping algorithms)."""
248
249
def unwrap_key(self, wrapped_key):
250
"""Unwrap a key (abstract method for key wrapping algorithms)."""
251
```
252
253
### HMAC Key Class
254
255
```python { .api }
256
class HMACKey(Key):
257
"""
258
HMAC key implementation for symmetric signing algorithms.
259
260
Supports: HS256, HS384, HS512
261
"""
262
263
def __init__(self, key_data, algorithm):
264
"""Initialize HMAC key with secret and algorithm."""
265
266
def to_dict(self):
267
"""
268
Convert key to JWK dictionary format.
269
270
Returns:
271
dict: JWK representation of the key
272
"""
273
274
def sign(self, msg):
275
"""Sign message with HMAC."""
276
277
def verify(self, msg, sig):
278
"""Verify HMAC signature."""
279
```
280
281
### RSA Key Class
282
283
```python { .api }
284
class RSAKey(Key):
285
"""
286
RSA key implementation for asymmetric operations.
287
288
Supports: RS256, RS384, RS512, PS256, PS384, PS512 (backend dependent)
289
Key wrapping: RSA1_5, RSA-OAEP, RSA-OAEP-256
290
"""
291
292
def __init__(self, key_data, algorithm):
293
"""Initialize RSA key from PEM data or JWK dictionary."""
294
295
def to_dict(self):
296
"""Convert to JWK dictionary format."""
297
298
def sign(self, msg):
299
"""Sign message with RSA private key."""
300
301
def verify(self, msg, sig):
302
"""Verify signature with RSA public key."""
303
304
def encrypt(self, plaintext):
305
"""Encrypt with RSA public key (for JWE)."""
306
307
def decrypt(self, ciphertext):
308
"""Decrypt with RSA private key (for JWE)."""
309
```
310
311
### EC Key Class
312
313
```python { .api }
314
class ECKey(Key):
315
"""
316
Elliptic Curve key implementation.
317
318
Supports: ES256, ES384, ES512
319
Curves: P-256, P-384, P-521
320
"""
321
322
def __init__(self, key_data, algorithm):
323
"""Initialize EC key from PEM data or JWK dictionary."""
324
325
def to_dict(self):
326
"""Convert to JWK dictionary format."""
327
328
def sign(self, msg):
329
"""Sign message with EC private key."""
330
331
def verify(self, msg, sig):
332
"""Verify signature with EC public key."""
333
```
334
335
### AES Key Class
336
337
```python { .api }
338
class AESKey(Key):
339
"""
340
AES key implementation for symmetric encryption.
341
342
Supports: A128KW, A192KW, A256KW (key wrapping)
343
A128GCM, A192GCM, A256GCM (content encryption)
344
"""
345
346
def __init__(self, key_data, algorithm):
347
"""Initialize AES key with key bytes."""
348
349
def encrypt(self, plaintext):
350
"""Encrypt with AES (algorithm-dependent mode)."""
351
352
def decrypt(self, ciphertext, **kwargs):
353
"""Decrypt with AES (algorithm-dependent mode)."""
354
355
def wrap_key(self, key_data):
356
"""Wrap key with AES key wrapping."""
357
358
def unwrap_key(self, wrapped_key):
359
"""Unwrap key with AES key wrapping."""
360
```
361
362
## JWK Dictionary Format
363
364
JWK (JSON Web Key) provides a standardized JSON representation for cryptographic keys:
365
366
### Symmetric Keys (HMAC)
367
368
```python { .api }
369
# HMAC key JWK format
370
hmac_jwk = {
371
'kty': 'oct', # Key type: octet sequence
372
'k': 'base64url-key', # Key value (base64url encoded)
373
'alg': 'HS256', # Algorithm (optional)
374
'use': 'sig', # Usage: sig (signature) or enc (encryption)
375
'kid': 'key-id' # Key ID (optional)
376
}
377
```
378
379
### RSA Keys
380
381
```python { .api }
382
# RSA private key JWK format
383
rsa_private_jwk = {
384
'kty': 'RSA', # Key type: RSA
385
'n': 'modulus-b64url', # Public modulus
386
'e': 'AQAB', # Public exponent (usually 65537)
387
'd': 'private-exp-b64url', # Private exponent
388
'p': 'prime1-b64url', # First prime factor
389
'q': 'prime2-b64url', # Second prime factor
390
'dp': 'exp1-b64url', # First factor exponent
391
'dq': 'exp2-b64url', # Second factor exponent
392
'qi': 'coefficient-b64url', # Coefficient
393
'alg': 'RS256', # Algorithm
394
'use': 'sig', # Usage
395
'kid': 'rsa-key-1' # Key ID
396
}
397
398
# RSA public key JWK format (subset of private)
399
rsa_public_jwk = {
400
'kty': 'RSA',
401
'n': 'modulus-b64url',
402
'e': 'AQAB',
403
'alg': 'RS256',
404
'use': 'sig',
405
'kid': 'rsa-key-1'
406
}
407
```
408
409
### Elliptic Curve Keys
410
411
```python { .api }
412
# EC private key JWK format
413
ec_private_jwk = {
414
'kty': 'EC', # Key type: Elliptic Curve
415
'crv': 'P-256', # Curve: P-256, P-384, or P-521
416
'x': 'x-coord-b64url', # X coordinate
417
'y': 'y-coord-b64url', # Y coordinate
418
'd': 'private-key-b64url', # Private key
419
'alg': 'ES256', # Algorithm
420
'use': 'sig', # Usage
421
'kid': 'ec-key-1' # Key ID
422
}
423
424
# EC public key JWK format (without 'd')
425
ec_public_jwk = {
426
'kty': 'EC',
427
'crv': 'P-256',
428
'x': 'x-coord-b64url',
429
'y': 'y-coord-b64url',
430
'alg': 'ES256',
431
'use': 'sig',
432
'kid': 'ec-key-1'
433
}
434
```
435
436
## Key Format Support
437
438
The JWK module supports various input key formats:
439
440
### PEM Format
441
442
```python
443
# RSA private key in PEM format
444
rsa_pem = """-----BEGIN PRIVATE KEY-----
445
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
446
-----END PRIVATE KEY-----"""
447
448
# RSA public key in PEM format
449
rsa_public_pem = """-----BEGIN PUBLIC KEY-----
450
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
451
-----END PUBLIC KEY-----"""
452
453
# EC private key in PEM format
454
ec_pem = """-----BEGIN PRIVATE KEY-----
455
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
456
-----END PRIVATE KEY-----"""
457
458
# Construct keys from PEM
459
rsa_key = jwk.construct(rsa_pem)
460
ec_key = jwk.construct(ec_pem)
461
```
462
463
### Raw Key Material
464
465
```python
466
# HMAC secret as string or bytes
467
hmac_secret = 'my-secret-key'
468
hmac_bytes = b'binary-secret-key'
469
470
# AES key as bytes (specific lengths)
471
aes_128_key = b'16-byte key here'
472
aes_256_key = b'This is a 32-byte key for AES256!!'
473
474
# Construct from raw material
475
hmac_key = jwk.construct(hmac_secret, algorithm='HS256')
476
aes_key = jwk.construct(aes_256_key, algorithm='A256GCM')
477
```
478
479
## Backend Integration
480
481
JWK automatically selects the best available cryptographic backend:
482
483
```python
484
# Backend priority (highest to lowest):
485
# 1. cryptography (pyca/cryptography) - Recommended
486
# 2. native-python (python-rsa, python-ecdsa)
487
# 3. pycryptodome (alternative backend)
488
489
# Check which backend is being used
490
key = jwk.construct('secret', algorithm='HS256')
491
print(type(key)) # Shows the backend class being used
492
493
# Examples of backend classes:
494
# - CryptographyHMACKey (cryptography backend)
495
# - CryptographyRSAKey (cryptography backend)
496
# - CryptographyECKey (cryptography backend)
497
# - RSAKey (native backend)
498
# - ECDSAECKey (native backend)
499
```
500
501
## Error Handling
502
503
JWK operations can raise specific exceptions:
504
505
```python { .api }
506
class JWKError(JOSEError):
507
"""Base exception for JWK-related errors."""
508
```
509
510
**Error Handling Examples:**
511
512
```python
513
from jose.exceptions import JWKError
514
515
try:
516
# Invalid key format
517
key = jwk.construct('invalid-key-format')
518
except JWKError as e:
519
print(f"Key construction failed: {e}")
520
521
try:
522
# Unsupported algorithm
523
key_class = jwk.get_key('UNKNOWN_ALGORITHM')
524
if key_class is None:
525
print("Algorithm not supported")
526
except JWKError as e:
527
print(f"Key retrieval failed: {e}")
528
529
try:
530
# Invalid JWK dictionary
531
invalid_jwk = {'kty': 'INVALID', 'k': 'key'}
532
key = jwk.construct(invalid_jwk)
533
except JWKError as e:
534
print(f"Invalid JWK: {e}")
535
```
536
537
## Usage with Other JOSE Operations
538
539
JWK objects integrate seamlessly with JWT, JWS, and JWE operations:
540
541
```python
542
from jose import jwt, jws, jwe, jwk
543
544
# Construct key once, use everywhere
545
key = jwk.construct('shared-secret', algorithm='HS256')
546
547
# Use with JWT
548
token = jwt.encode({'user': 'john'}, key)
549
claims = jwt.decode(token, key, algorithms=['HS256'])
550
551
# Use with JWS
552
signature = jws.sign('message', key)
553
payload = jws.verify(signature, key, algorithms=['HS256'])
554
555
# For JWE (with appropriate key type)
556
aes_key = jwk.construct(b'32-byte-key-for-aes-256-gcm!!!!', algorithm='A256GCM')
557
encrypted = jwe.encrypt(b'secret', aes_key, algorithm='dir')
558
plaintext = jwe.decrypt(encrypted, aes_key)
559
```
560
561
## Best Practices
562
563
1. **Key Generation**: Use cryptographically secure random sources for key generation
564
2. **Key Storage**: Store keys securely, separate from application code
565
3. **Key Rotation**: Implement regular key rotation with overlapping validity periods
566
4. **Algorithm Selection**: Use strong algorithms (HS256+, RS256+, ES256+)
567
5. **Key Size**: Use appropriate key sizes (256-bit HMAC, 2048+ bit RSA, P-256+ EC)
568
6. **JWK Format**: Use JWK format for key distribution and storage when possible
569
7. **Backend Selection**: Prefer cryptography backend for production environments
570
8. **Error Handling**: Always handle JWKError exceptions in key operations