0
# JWE Operations
1
2
JSON Web Encryption functionality for encrypting and decrypting content using industry-standard encryption algorithms and key management methods. JWE provides confidentiality for sensitive data through authenticated encryption.
3
4
## Capabilities
5
6
### Content Encryption
7
8
Encrypts plaintext content and returns JWE compact serialization format with support for various encryption algorithms and key management methods.
9
10
```python { .api }
11
def encrypt(plaintext, key, encryption='A256GCM', algorithm='dir', zip=None, cty=None, kid=None):
12
"""
13
Encrypts plaintext and returns a JWE compact serialization string.
14
15
Args:
16
plaintext (bytes): The data to encrypt. Must be bytes object.
17
Use encode() to convert strings to bytes
18
key (str or bytes or dict): The encryption key. Supports:
19
- Bytes for direct encryption (dir algorithm)
20
- RSA public keys in PEM format for RSA key wrapping
21
- JWK dictionaries with appropriate key material
22
encryption (str): The content encryption algorithm. Defaults to 'A256GCM'.
23
Supported: A128GCM, A192GCM, A256GCM, A128CBC-HS256, A192CBC-HS384, A256CBC-HS512
24
algorithm (str): The key management algorithm. Defaults to 'dir'.
25
Supported: dir, RSA1_5, RSA-OAEP, RSA-OAEP-256, A128KW, A192KW, A256KW
26
zip (str, optional): The compression algorithm applied before encryption.
27
Supported: 'DEF' for DEFLATE compression, None for no compression
28
cty (str, optional): The content type of the secured content.
29
Examples: 'application/json', 'text/plain'
30
kid (str, optional): Key ID hint for the recipient to identify the key
31
32
Returns:
33
bytes: The JWE token in compact format (header.encryptedkey.iv.ciphertext.tag)
34
35
Raises:
36
JWEError: If encryption fails or parameters are invalid
37
JWEAlgorithmUnsupportedError: If the specified algorithm is not supported
38
"""
39
```
40
41
**Usage Examples:**
42
43
```python
44
from jose import jwe
45
from jose.constants import ALGORITHMS
46
47
# Basic AES-GCM encryption with direct key
48
key = b'This is a 32-byte key for AES256!!' # 32 bytes for A256GCM
49
plaintext = b'Secret message'
50
encrypted = jwe.encrypt(plaintext, key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)
51
52
# String to bytes conversion
53
message = "Confidential data"
54
encrypted = jwe.encrypt(message.encode('utf-8'), key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)
55
56
# RSA key wrapping with OAEP
57
rsa_public_key = """-----BEGIN PUBLIC KEY-----
58
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
59
-----END PUBLIC KEY-----"""
60
61
encrypted = jwe.encrypt(
62
b'Sensitive data',
63
rsa_public_key,
64
encryption=ALGORITHMS.A256GCM,
65
algorithm=ALGORITHMS.RSA_OAEP
66
)
67
68
# With compression
69
encrypted = jwe.encrypt(
70
b'Large repetitive data that compresses well...',
71
key,
72
encryption=ALGORITHMS.A256GCM,
73
algorithm=ALGORITHMS.DIR,
74
zip='DEF' # DEFLATE compression
75
)
76
77
# With content type and key ID
78
encrypted = jwe.encrypt(
79
b'{"user": "john", "role": "admin"}',
80
key,
81
encryption=ALGORITHMS.A256GCM,
82
algorithm=ALGORITHMS.DIR,
83
cty='application/json',
84
kid='encryption-key-1'
85
)
86
87
# AES key wrapping
88
aes_kek = b'Key Encryption Key 32 bytes!!!!!' # 32 bytes for A256KW
89
encrypted = jwe.encrypt(
90
b'Protected content',
91
aes_kek,
92
encryption=ALGORITHMS.A256GCM,
93
algorithm=ALGORITHMS.A256KW
94
)
95
96
# CBC with HMAC authentication
97
encrypted = jwe.encrypt(
98
b'Legacy compatibility data',
99
key,
100
encryption=ALGORITHMS.A256CBC_HS512,
101
algorithm=ALGORITHMS.DIR
102
)
103
```
104
105
### Content Decryption
106
107
Decrypts JWE tokens and returns the original plaintext after verifying authentication.
108
109
```python { .api }
110
def decrypt(jwe_str, key):
111
"""
112
Decrypts a JWE token and returns the original plaintext.
113
114
Args:
115
jwe_str (str or bytes): The JWE token to decrypt in compact format
116
key (str or bytes or dict): The decryption key. Must match the key
117
used for encryption and be appropriate for the key management algorithm:
118
- Bytes for direct encryption (dir algorithm)
119
- RSA private keys in PEM format for RSA key wrapping
120
- AES keys for AES key wrapping
121
- JWK dictionaries with appropriate key material
122
123
Returns:
124
bytes: The decrypted plaintext as bytes. Use decode() to convert to string
125
126
Raises:
127
JWEError: If decryption fails or token format is invalid
128
JWEParseError: If the JWE token cannot be parsed
129
JWEInvalidAuth: If authentication verification fails
130
"""
131
```
132
133
**Usage Examples:**
134
135
```python
136
from jose import jwe
137
from jose.exceptions import JWEError, JWEParseError, JWEInvalidAuth
138
139
try:
140
# Basic decryption
141
plaintext = jwe.decrypt(encrypted_token, key)
142
message = plaintext.decode('utf-8')
143
print(f"Decrypted: {message}")
144
145
except JWEInvalidAuth:
146
print("Authentication verification failed - data may be tampered")
147
except JWEParseError:
148
print("Invalid JWE token format")
149
except JWEError as e:
150
print(f"Decryption failed: {e}")
151
152
# RSA key unwrapping
153
rsa_private_key = """-----BEGIN PRIVATE KEY-----
154
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
155
-----END PRIVATE KEY-----"""
156
157
plaintext = jwe.decrypt(encrypted_token, rsa_private_key)
158
159
# AES key unwrapping
160
aes_kek = b'Key Encryption Key 32 bytes!!!!!'
161
plaintext = jwe.decrypt(encrypted_token, aes_kek)
162
163
# Direct key decryption
164
direct_key = b'This is a 32-byte key for AES256!!'
165
plaintext = jwe.decrypt(encrypted_token, direct_key)
166
167
# JWK dictionary key
168
jwk_key = {
169
'kty': 'oct',
170
'k': 'VGhpcyBpcyBhIDMyLWJ5dGUga2V5IGZvciBBRVMyNTYhIQ' # base64url encoded
171
}
172
plaintext = jwe.decrypt(encrypted_token, jwk_key)
173
```
174
175
### Header Inspection
176
177
Retrieve JWE header information without performing decryption.
178
179
```python { .api }
180
def get_unverified_header(jwe_str):
181
"""
182
Get JWE header without verification or decryption.
183
184
Args:
185
jwe_str (str or bytes): The JWE token
186
187
Returns:
188
dict: The JWE header dictionary
189
190
Raises:
191
JWEParseError: If the JWE token format is invalid
192
"""
193
```
194
195
**Usage Examples:**
196
197
```python
198
# Inspect JWE header
199
header = jwe.get_unverified_header(encrypted_token)
200
print(f"Key Management Algorithm: {header['alg']}")
201
print(f"Content Encryption Algorithm: {header['enc']}")
202
print(f"Key ID: {header.get('kid')}")
203
print(f"Content Type: {header.get('cty')}")
204
print(f"Compression: {header.get('zip')}")
205
206
# Key selection based on header
207
header = jwe.get_unverified_header(encrypted_token)
208
key_id = header.get('kid')
209
if key_id == 'rsa-key-1':
210
key = rsa_private_key_1
211
elif key_id == 'rsa-key-2':
212
key = rsa_private_key_2
213
else:
214
key = default_key
215
216
plaintext = jwe.decrypt(encrypted_token, key)
217
```
218
219
## JWE Header Structure
220
221
JWE headers contain metadata about encryption algorithms and key management:
222
223
```python { .api }
224
# Standard JWE header fields
225
header = {
226
'alg': 'dir', # Key management algorithm (required)
227
'enc': 'A256GCM', # Content encryption algorithm (required)
228
'zip': 'DEF', # Compression algorithm (optional)
229
'kid': 'key-1', # Key ID for key identification (optional)
230
'cty': 'application/json', # Content type (optional)
231
'typ': 'JWE' # Type (optional)
232
}
233
```
234
235
## Encryption Algorithms
236
237
JWE supports multiple content encryption algorithms:
238
239
**AES-GCM (Authenticated Encryption):**
240
- `A128GCM`: AES-128-GCM (16-byte key)
241
- `A192GCM`: AES-192-GCM (24-byte key)
242
- `A256GCM`: AES-256-GCM (32-byte key) - **Recommended**
243
244
**AES-CBC with HMAC (Legacy Compatibility):**
245
- `A128CBC-HS256`: AES-128-CBC + HMAC-SHA-256
246
- `A192CBC-HS384`: AES-192-CBC + HMAC-SHA-384
247
- `A256CBC-HS512`: AES-256-CBC + HMAC-SHA-512
248
249
```python
250
from jose.constants import ALGORITHMS
251
252
# Modern authenticated encryption (recommended)
253
encrypted = jwe.encrypt(data, key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)
254
255
# Legacy compatibility
256
encrypted = jwe.encrypt(data, key, ALGORITHMS.A256CBC_HS512, ALGORITHMS.DIR)
257
258
# Different key sizes
259
encrypted = jwe.encrypt(data, key_16, ALGORITHMS.A128GCM, ALGORITHMS.DIR) # 16-byte key
260
encrypted = jwe.encrypt(data, key_24, ALGORITHMS.A192GCM, ALGORITHMS.DIR) # 24-byte key
261
encrypted = jwe.encrypt(data, key_32, ALGORITHMS.A256GCM, ALGORITHMS.DIR) # 32-byte key
262
```
263
264
## Key Management Algorithms
265
266
JWE supports various key management methods:
267
268
**Direct Encryption:**
269
- `dir`: Direct use of Content Encryption Key (CEK)
270
271
**RSA Key Wrapping:**
272
- `RSA1_5`: RSA PKCS#1 v1.5 (legacy, not recommended)
273
- `RSA-OAEP`: RSA OAEP with SHA-1 and MGF1
274
- `RSA-OAEP-256`: RSA OAEP with SHA-256 and MGF1 - **Recommended**
275
276
**AES Key Wrapping:**
277
- `A128KW`: AES-128 Key Wrap
278
- `A192KW`: AES-192 Key Wrap
279
- `A256KW`: AES-256 Key Wrap
280
281
```python
282
# Direct encryption (CEK = key)
283
encrypted = jwe.encrypt(plaintext, direct_key, algorithm=ALGORITHMS.DIR)
284
285
# RSA key wrapping (recommended OAEP-256)
286
encrypted = jwe.encrypt(plaintext, rsa_public_key, algorithm=ALGORITHMS.RSA_OAEP_256)
287
288
# AES key wrapping
289
encrypted = jwe.encrypt(plaintext, aes_kek, algorithm=ALGORITHMS.A256KW)
290
```
291
292
## Key Requirements
293
294
Different algorithms require specific key formats and sizes:
295
296
```python
297
# Direct encryption key requirements
298
key_128 = b'16-byte key here' # For A128GCM
299
key_192 = b'24-byte key here!!!!!!!!' # For A192GCM
300
key_256 = b'This is a 32-byte key for AES256!!' # For A256GCM
301
302
# RSA keys (PEM format)
303
rsa_public_key = """-----BEGIN PUBLIC KEY-----
304
... (RSA public key in PEM format for encryption)
305
-----END PUBLIC KEY-----"""
306
307
rsa_private_key = """-----BEGIN PRIVATE KEY-----
308
... (RSA private key in PEM format for decryption)
309
-----END PRIVATE KEY-----"""
310
311
# AES Key Encryption Keys (KEK)
312
aes_kek_128 = b'16-byte KEK here' # For A128KW
313
aes_kek_192 = b'24-byte KEK here!!!!!!!' # For A192KW
314
aes_kek_256 = b'32-byte KEK for wrapping!!!!!!!!' # For A256KW
315
```
316
317
## Compression Support
318
319
JWE supports DEFLATE compression to reduce ciphertext size:
320
321
```python
322
# With compression (useful for large or repetitive data)
323
large_data = b'{"users": [' + b'{"name": "user", "role": "admin"},' * 1000 + b']}'
324
encrypted = jwe.encrypt(
325
large_data,
326
key,
327
encryption=ALGORITHMS.A256GCM,
328
algorithm=ALGORITHMS.DIR,
329
zip='DEF' # DEFLATE compression
330
)
331
332
# Compression is transparent during decryption
333
decrypted = jwe.decrypt(encrypted, key) # Automatically decompressed
334
```
335
336
## Error Handling
337
338
JWE operations can raise several specific exceptions:
339
340
```python { .api }
341
class JWEError(JOSEError):
342
"""Base exception for JWE-related errors."""
343
344
class JWEParseError(JWEError):
345
"""JWE token parsing failed."""
346
347
class JWEInvalidAuth(JWEError):
348
"""JWE authentication verification failed."""
349
350
class JWEAlgorithmUnsupportedError(JWEError):
351
"""JWE algorithm not supported."""
352
```
353
354
**Error Handling Examples:**
355
356
```python
357
from jose.exceptions import JWEError, JWEParseError, JWEInvalidAuth, JWEAlgorithmUnsupportedError
358
359
try:
360
plaintext = jwe.decrypt(encrypted_token, key)
361
except JWEInvalidAuth:
362
print("Authentication failed - data may be corrupted or tampered")
363
except JWEParseError:
364
print("Invalid JWE token format")
365
except JWEAlgorithmUnsupportedError:
366
print("Encryption algorithm not supported by current backend")
367
except JWEError as e:
368
print(f"JWE operation failed: {e}")
369
370
# Encryption errors
371
try:
372
encrypted = jwe.encrypt(plaintext, key, 'UNSUPPORTED_ALG', 'dir')
373
except JWEAlgorithmUnsupportedError:
374
print("Unsupported encryption algorithm")
375
except JWEError as e:
376
print(f"Encryption failed: {e}")
377
```
378
379
## Security Considerations
380
381
1. **Algorithm Selection**: Use A256GCM with RSA-OAEP-256 for new applications
382
2. **Key Management**: Generate cryptographically secure random keys
383
3. **Key Size**: Use appropriate key sizes (256-bit for AES, 2048+ bit for RSA)
384
4. **Authentication**: Always verify authentication (handled automatically)
385
5. **Key Storage**: Store encryption keys securely, separate from encrypted data
386
6. **Key Rotation**: Implement key rotation for long-term security
387
388
## Integration with Other Modules
389
390
JWE works with other jose modules for complete JOSE functionality:
391
392
```python
393
from jose import jwe, jwk
394
395
# Use JWK for key management
396
key = jwk.construct({'kty': 'oct', 'k': 'encoded-key'})
397
encrypted = jwe.encrypt(plaintext, key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)
398
399
# Nested JWT (signed then encrypted)
400
from jose import jwt
401
# First sign with JWT
402
signed_token = jwt.encode(claims, signing_key, algorithm='HS256')
403
# Then encrypt the JWT
404
encrypted_jwt = jwe.encrypt(signed_token.encode(), encryption_key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)
405
406
# Decrypt then verify
407
decrypted_jwt = jwe.decrypt(encrypted_jwt, encryption_key)
408
claims = jwt.decode(decrypted_jwt.decode(), signing_key, algorithms=['HS256'])
409
```
410
411
## Best Practices
412
413
1. **Use authenticated encryption**: Prefer A256GCM over CBC+HMAC modes
414
2. **Strong key management**: Use RSA-OAEP-256 or secure key distribution
415
3. **Key rotation**: Implement regular key rotation policies
416
4. **Secure random keys**: Generate keys using cryptographically secure random sources
417
5. **Validate inputs**: Always validate plaintext size (max 250KB) and key formats
418
6. **Handle errors gracefully**: Implement proper error handling for all failure modes
419
7. **Compression consideration**: Use compression only when beneficial for data size