0
# JSON Web Keys (JWK)
1
2
Key representation and management implementing the JSON Web Key specification (RFC 7517). Provides unified interfaces for RSA, Elliptic Curve, and symmetric keys with support for key loading, thumbprint generation, and public key extraction.
3
4
## Capabilities
5
6
### Base JWK Class
7
8
Abstract base class providing common functionality for all JSON Web Key types.
9
10
```python { .api }
11
class JWK:
12
"""Base class for JSON Web Keys"""
13
14
def thumbprint(self, hash_function=hashes.SHA256) -> bytes:
15
"""
16
Compute JWK Thumbprint according to RFC 7638.
17
18
Parameters:
19
- hash_function: Hash algorithm factory (default: SHA256)
20
21
Returns:
22
bytes: Key thumbprint hash
23
"""
24
25
def public_key(self) -> 'JWK':
26
"""
27
Generate JWK containing only the public key.
28
For symmetric keys, returns self.
29
30
Returns:
31
JWK: Public key JWK instance
32
"""
33
34
@classmethod
35
def from_json(cls, jobj: Any) -> 'JWK':
36
"""Deserialize JWK from JSON object"""
37
38
def to_partial_json(self) -> Any:
39
"""Serialize JWK to JSON-compatible dictionary"""
40
41
@classmethod
42
def load(cls, data: bytes, password: Optional[bytes] = None, backend: Optional[Any] = None) -> 'JWK':
43
"""
44
Load serialized key as JWK with automatic type detection.
45
46
Parameters:
47
- data: Public or private key serialized as PEM or DER
48
- password: Optional password for encrypted private keys
49
- backend: Cryptography backend (defaults to default_backend())
50
51
Returns:
52
JWK: JWK instance of appropriate type (JWKRSA, JWKEC, or JWKOct)
53
54
Raises:
55
josepy.errors.Error: If unable to deserialize or unsupported algorithm
56
"""
57
58
@classmethod
59
def _load_cryptography_key(cls, data: bytes, password: Optional[bytes] = None, backend: Optional[Any] = None) -> Any:
60
"""Load cryptography key from PEM/DER data"""
61
62
key: Any # Underlying cryptography key object
63
```
64
65
### RSA Keys (JWKRSA)
66
67
RSA key representation supporting both private and public keys with PKCS#1 and PSS algorithms.
68
69
```python { .api }
70
class JWKRSA(JWK):
71
"""RSA JSON Web Key"""
72
73
def __init__(self, key=None):
74
"""
75
Initialize RSA JWK.
76
77
Parameters:
78
- key: RSA private or public key from cryptography library,
79
or bytes/str containing PEM/DER encoded key data
80
"""
81
82
@classmethod
83
def load(cls, data: bytes, password: Optional[bytes] = None) -> 'JWKRSA':
84
"""
85
Load RSA key from PEM or DER encoded data.
86
87
Parameters:
88
- data: PEM or DER encoded key data
89
- password: Password for encrypted private keys
90
91
Returns:
92
JWKRSA: RSA JWK instance
93
"""
94
95
def public_key(self) -> 'JWKRSA':
96
"""Extract public key component"""
97
98
# JSON field mappings for RSA parameters
99
n: bytes # Modulus
100
e: bytes # Public exponent
101
d: bytes # Private exponent (private keys only)
102
p: bytes # First prime factor (private keys only)
103
q: bytes # Second prime factor (private keys only)
104
dp: bytes # First factor CRT exponent (private keys only)
105
dq: bytes # Second factor CRT exponent (private keys only)
106
qi: bytes # First CRT coefficient (private keys only)
107
```
108
109
#### Usage Examples
110
111
```python
112
from josepy import JWKRSA
113
from cryptography.hazmat.primitives.asymmetric import rsa
114
from cryptography.hazmat.backends import default_backend
115
from cryptography.hazmat.primitives import serialization
116
117
# Generate new RSA key
118
private_key = rsa.generate_private_key(
119
public_exponent=65537,
120
key_size=2048,
121
backend=default_backend()
122
)
123
124
# Create JWK from cryptography key
125
jwk = JWKRSA(key=private_key)
126
127
# Get public key JWK
128
public_jwk = jwk.public_key()
129
130
# Compute thumbprint
131
thumbprint = jwk.thumbprint()
132
print(f"Key thumbprint: {thumbprint.hex()}")
133
134
# Serialize to JSON
135
jwk_json = jwk.json_dumps()
136
print(f"JWK JSON: {jwk_json}")
137
138
# Load from JSON
139
loaded_jwk = JWKRSA.json_loads(jwk_json)
140
141
# Load from PEM data
142
pem_data = private_key.private_bytes(
143
encoding=serialization.Encoding.PEM,
144
format=serialization.PrivateFormat.PKCS8,
145
encryption_algorithm=serialization.NoEncryption()
146
)
147
jwk_from_pem = JWKRSA.load(pem_data)
148
```
149
150
### Elliptic Curve Keys (JWKEC)
151
152
Elliptic Curve key representation supporting NIST P-curves for ECDSA operations.
153
154
```python { .api }
155
class JWKEC(JWK):
156
"""Elliptic Curve JSON Web Key"""
157
158
def __init__(self, key=None):
159
"""
160
Initialize EC JWK.
161
162
Parameters:
163
- key: EC private or public key from cryptography library,
164
or bytes/str containing PEM/DER encoded key data
165
"""
166
167
@classmethod
168
def load(cls, data: bytes, password: Optional[bytes] = None) -> 'JWKEC':
169
"""
170
Load EC key from PEM or DER encoded data.
171
172
Parameters:
173
- data: PEM or DER encoded key data
174
- password: Password for encrypted private keys
175
176
Returns:
177
JWKEC: EC JWK instance
178
"""
179
180
def public_key(self) -> 'JWKEC':
181
"""Extract public key component"""
182
183
# JSON field mappings for EC parameters
184
crv: str # Curve name ("P-256", "P-384", "P-521")
185
x: bytes # X coordinate
186
y: bytes # Y coordinate
187
d: bytes # Private key value (private keys only)
188
```
189
190
#### Usage Examples
191
192
```python
193
from josepy import JWKEC
194
from cryptography.hazmat.primitives.asymmetric import ec
195
from cryptography.hazmat.backends import default_backend
196
197
# Generate new EC key (P-256)
198
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
199
200
# Create JWK from cryptography key
201
jwk_ec = JWKEC(key=private_key)
202
203
# Get public key JWK
204
public_jwk_ec = jwk_ec.public_key()
205
206
# Compute thumbprint
207
thumbprint_ec = jwk_ec.thumbprint()
208
print(f"EC Key thumbprint: {thumbprint_ec.hex()}")
209
210
# Different curves
211
p384_key = ec.generate_private_key(ec.SECP384R1(), default_backend())
212
jwk_p384 = JWKEC(key=p384_key)
213
214
p521_key = ec.generate_private_key(ec.SECP521R1(), default_backend())
215
jwk_p521 = JWKEC(key=p521_key)
216
```
217
218
### Symmetric Keys (JWKOct)
219
220
Octet string key representation for symmetric cryptographic operations including HMAC.
221
222
```python { .api }
223
class JWKOct(JWK):
224
"""Symmetric (Octet String) JSON Web Key"""
225
226
def __init__(self, key=None):
227
"""
228
Initialize symmetric JWK.
229
230
Parameters:
231
- key: bytes containing the symmetric key material
232
"""
233
234
def public_key(self) -> 'JWKOct':
235
"""For symmetric keys, returns self"""
236
237
# JSON field mappings
238
k: bytes # Key value (base64url encoded in JSON)
239
```
240
241
#### Usage Examples
242
243
```python
244
from josepy import JWKOct
245
import os
246
247
# Generate random symmetric key
248
key_bytes = os.urandom(32) # 256-bit key
249
jwk_oct = JWKOct(key=key_bytes)
250
251
# Symmetric keys are their own "public key"
252
same_key = jwk_oct.public_key()
253
assert jwk_oct is same_key
254
255
# Compute thumbprint
256
thumbprint_oct = jwk_oct.thumbprint()
257
print(f"Symmetric key thumbprint: {thumbprint_oct.hex()}")
258
259
# Use with HMAC algorithms
260
from josepy import HS256
261
message = b"Hello, HMAC!"
262
signature = HS256.sign(jwk_oct.key, message)
263
is_valid = HS256.verify(jwk_oct.key, message, signature)
264
print(f"HMAC signature valid: {is_valid}")
265
```
266
267
## Key Loading and Generation
268
269
### Loading Keys from Various Formats
270
271
```python
272
from josepy import JWKRSA, JWKEC
273
274
# Load from PEM files
275
with open('private_key.pem', 'rb') as f:
276
pem_data = f.read()
277
jwk_rsa = JWKRSA.load(pem_data)
278
279
# Load password-protected keys
280
with open('encrypted_key.pem', 'rb') as f:
281
encrypted_pem = f.read()
282
jwk_rsa = JWKRSA.load(encrypted_pem, password=b'secret')
283
284
# Load from DER format
285
with open('key.der', 'rb') as f:
286
der_data = f.read()
287
jwk_rsa = JWKRSA.load(der_data)
288
289
# Load EC keys
290
with open('ec_key.pem', 'rb') as f:
291
ec_pem = f.read()
292
jwk_ec = JWKEC.load(ec_pem)
293
```
294
295
### JSON Serialization and Deserialization
296
297
```python
298
from josepy import JWKRSA, JWKEC, JWKOct
299
300
# Serialize any JWK to JSON
301
jwk_json = jwk.json_dumps(indent=2)
302
303
# Deserialize from JSON (automatic type detection)
304
from josepy import JWK
305
loaded_jwk = JWK.from_json(json.loads(jwk_json))
306
307
# Type-specific deserialization
308
rsa_jwk = JWKRSA.json_loads(jwk_json)
309
ec_jwk = JWKEC.json_loads(jwk_json)
310
oct_jwk = JWKOct.json_loads(jwk_json)
311
```
312
313
## Key Thumbprints
314
315
JWK thumbprints provide unique identifiers for keys according to RFC 7638:
316
317
```python
318
from josepy import JWKRSA
319
from cryptography.hazmat.primitives import hashes
320
321
# Default SHA-256 thumbprint
322
thumbprint = jwk.thumbprint()
323
324
# Custom hash function
325
thumbprint_sha1 = jwk.thumbprint(hashes.SHA1)
326
thumbprint_sha512 = jwk.thumbprint(hashes.SHA512)
327
328
# Thumbprints are deterministic and unique per key
329
assert jwk.thumbprint() == jwk.thumbprint()
330
assert public_jwk.thumbprint() == jwk.thumbprint() # Same for public key
331
```
332
333
## Error Handling
334
335
```python
336
from josepy.errors import DeserializationError
337
338
try:
339
# Invalid JSON structure
340
jwk = JWKRSA.json_loads('{"invalid": "json"}')
341
except DeserializationError as e:
342
print(f"Deserialization failed: {e}")
343
344
try:
345
# Invalid key data
346
jwk = JWKRSA.load(b"not a valid key")
347
except (ValueError, TypeError) as e:
348
print(f"Key loading failed: {e}")
349
```