0
# JWS Operations
1
2
Low-level JSON Web Signature functionality for signing arbitrary payloads and verifying signatures. JWS provides the cryptographic foundation for JWT tokens and enables signing of any content, not just JSON claims.
3
4
## Capabilities
5
6
### Payload Signing
7
8
Signs arbitrary payloads using various algorithms and returns JWS compact serialization format.
9
10
```python { .api }
11
def sign(payload, key, headers=None, algorithm='HS256'):
12
"""
13
Signs a payload and returns a JWS string.
14
15
Args:
16
payload (str or dict): A payload to sign. Can be:
17
- String data to sign directly
18
- Dictionary that will be JSON-encoded before signing
19
key (str or bytes or dict): The key to use for signing. Supports:
20
- String secrets for HMAC algorithms
21
- RSA/EC private keys in PEM format
22
- JWK dictionaries
23
- Key objects from jose.jwk
24
headers (dict, optional): Additional headers to include in the JWS header.
25
Will be merged with default headers (alg, typ)
26
algorithm (str): The algorithm to use for signing. Defaults to 'HS256'.
27
Supported: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
28
29
Returns:
30
str: The JWS token in compact format (header.payload.signature)
31
32
Raises:
33
JWSError: If there is an error signing the payload or algorithm not supported
34
"""
35
```
36
37
**Usage Examples:**
38
39
```python
40
from jose import jws
41
from jose.constants import ALGORITHMS
42
43
# Sign a string payload with HMAC
44
payload = "Hello, World!"
45
token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256)
46
47
# Sign a dictionary payload (auto JSON-encoded)
48
payload = {'message': 'Hello', 'timestamp': 1234567890}
49
token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256)
50
51
# Sign with additional headers
52
headers = {'kid': 'key-1', 'cty': 'text/plain'}
53
token = jws.sign("Important message", 'secret', headers=headers)
54
55
# RSA signing with private key
56
rsa_private_key = """-----BEGIN PRIVATE KEY-----
57
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
58
-----END PRIVATE KEY-----"""
59
60
token = jws.sign("Secure message", rsa_private_key, algorithm=ALGORITHMS.RS256)
61
62
# ECDSA signing
63
ec_private_key = """-----BEGIN PRIVATE KEY-----
64
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
65
-----END PRIVATE KEY-----"""
66
67
token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES256)
68
69
# Using JWK dictionary
70
jwk_key = {
71
'kty': 'oct',
72
'k': 'GawgguFyGrWKav7AX4VKUg' # base64url-encoded secret
73
}
74
token = jws.sign(payload, jwk_key, algorithm=ALGORITHMS.HS256)
75
```
76
77
### Signature Verification
78
79
Verifies JWS signatures and returns the original payload after successful verification.
80
81
```python { .api }
82
def verify(token, key, algorithms, verify=True):
83
"""
84
Verifies a JWS token and returns the payload.
85
86
Args:
87
token (str or bytes): The JWS token to verify in compact format
88
key (str or bytes or dict or list): The verification key(s). Supports:
89
- String secrets for HMAC algorithms
90
- RSA/EC public keys in PEM format
91
- JWK dictionaries
92
- List of keys to try multiple verification keys
93
- Key objects from jose.jwk
94
algorithms (str or list): Allowed algorithms for verification. Required
95
for security - specify exactly which algorithms you accept
96
verify (bool): Whether to verify the signature. Defaults to True.
97
Set to False only for debugging or when signature is verified elsewhere
98
99
Returns:
100
bytes: The verified payload as bytes
101
102
Raises:
103
JWSError: If verification fails or token format is invalid
104
JWSSignatureError: If signature verification specifically fails
105
"""
106
```
107
108
**Usage Examples:**
109
110
```python
111
from jose import jws
112
from jose.exceptions import JWSError, JWSSignatureError
113
114
try:
115
# Basic HMAC verification
116
payload = jws.verify(token, 'secret', algorithms=['HS256'])
117
print(payload.decode('utf-8')) # Convert bytes to string
118
119
except JWSSignatureError:
120
print("Signature verification failed")
121
except JWSError as e:
122
print(f"JWS verification error: {e}")
123
124
# RSA public key verification
125
rsa_public_key = """-----BEGIN PUBLIC KEY-----
126
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
127
-----END PUBLIC KEY-----"""
128
129
payload = jws.verify(token, rsa_public_key, algorithms=['RS256'])
130
131
# Multiple algorithm support
132
payload = jws.verify(token, key, algorithms=['HS256', 'HS384', 'HS512'])
133
134
# Multiple key verification (try different keys)
135
keys = [
136
'primary-secret',
137
'backup-secret',
138
{'kty': 'oct', 'k': 'encoded-key'}
139
]
140
payload = jws.verify(token, keys, algorithms=['HS256'])
141
142
# Skip signature verification (debugging only)
143
payload = jws.verify(token, '', algorithms=['HS256'], verify=False)
144
145
# EC public key verification
146
ec_public_key = """-----BEGIN PUBLIC KEY-----
147
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
148
-----END PUBLIC KEY-----"""
149
150
payload = jws.verify(token, ec_public_key, algorithms=['ES256'])
151
```
152
153
### Header and Payload Inspection
154
155
Retrieve JWS headers and payload without performing signature verification.
156
157
```python { .api }
158
def get_unverified_header(token):
159
"""
160
Get JWS header without verification.
161
162
Args:
163
token (str or bytes): The JWS token
164
165
Returns:
166
dict: The JWS header dictionary
167
168
Raises:
169
JWSError: If the token format is invalid
170
"""
171
172
def get_unverified_headers(token):
173
"""
174
Get JWS header without verification (alias for get_unverified_header).
175
176
Args:
177
token (str or bytes): The JWS token
178
179
Returns:
180
dict: The JWS header dictionary
181
182
Raises:
183
JWSError: If the token format is invalid
184
"""
185
186
def get_unverified_claims(token):
187
"""
188
Get JWS payload without verification.
189
190
Args:
191
token (str or bytes): The JWS token
192
193
Returns:
194
bytes: The JWS payload as bytes
195
196
Raises:
197
JWSError: If the token format is invalid
198
"""
199
```
200
201
**Usage Examples:**
202
203
```python
204
# Inspect JWS header
205
header = jws.get_unverified_header(token)
206
print(f"Algorithm: {header['alg']}")
207
print(f"Key ID: {header.get('kid')}")
208
print(f"Content Type: {header.get('cty')}")
209
210
# Get payload without verification
211
payload_bytes = jws.get_unverified_claims(token)
212
payload_str = payload_bytes.decode('utf-8')
213
print(f"Payload: {payload_str}")
214
215
# Conditional verification based on algorithm
216
header = jws.get_unverified_header(token)
217
if header['alg'] in ['HS256', 'HS384', 'HS512']:
218
payload = jws.verify(token, hmac_secret, algorithms=[header['alg']])
219
elif header['alg'] in ['RS256', 'RS384', 'RS512']:
220
payload = jws.verify(token, rsa_public_key, algorithms=[header['alg']])
221
elif header['alg'] in ['ES256', 'ES384', 'ES512']:
222
payload = jws.verify(token, ec_public_key, algorithms=[header['alg']])
223
```
224
225
## JWS Header Structure
226
227
JWS headers contain metadata about the signature algorithm and optional additional information:
228
229
```python { .api }
230
# Standard JWS header fields
231
header = {
232
'alg': 'HS256', # Algorithm (required)
233
'typ': 'JWT', # Type (optional, commonly 'JWT')
234
'kid': 'key-identifier', # Key ID for key selection (optional)
235
'cty': 'application/json', # Content type (optional)
236
'crit': ['custom-claim'] # Critical header parameters (optional)
237
}
238
```
239
240
**Header Examples:**
241
242
```python
243
# Minimal header (algorithm only)
244
headers = {'alg': 'HS256'}
245
246
# Header with key identification
247
headers = {'alg': 'RS256', 'kid': 'rsa-key-1'}
248
249
# Header with content type for non-JSON payloads
250
headers = {'alg': 'HS256', 'cty': 'text/plain'}
251
252
# Custom headers (use with caution)
253
headers = {
254
'alg': 'HS256',
255
'custom': 'value',
256
'x-app-version': '1.0.0'
257
}
258
```
259
260
## Payload Formats
261
262
JWS can sign various payload formats:
263
264
```python
265
# String payloads
266
payload = "Plain text message"
267
token = jws.sign(payload, key)
268
269
# JSON payloads (dictionaries)
270
payload = {'user': 'john', 'action': 'login'}
271
token = jws.sign(payload, key) # Auto-converted to JSON
272
273
# Binary payloads
274
payload = b"Binary data\x00\x01\x02"
275
token = jws.sign(payload, key)
276
277
# Pre-encoded JSON strings
278
import json
279
payload = json.dumps({'data': 'value'})
280
token = jws.sign(payload, key)
281
```
282
283
## Algorithm Support
284
285
JWS supports multiple signature algorithms:
286
287
**HMAC Algorithms (Symmetric):**
288
- `HS256`: HMAC using SHA-256 hash
289
- `HS384`: HMAC using SHA-384 hash
290
- `HS512`: HMAC using SHA-512 hash
291
292
**RSA Algorithms (Asymmetric):**
293
- `RS256`: RSA PKCS#1 v1.5 using SHA-256
294
- `RS384`: RSA PKCS#1 v1.5 using SHA-384
295
- `RS512`: RSA PKCS#1 v1.5 using SHA-512
296
297
**ECDSA Algorithms (Asymmetric):**
298
- `ES256`: ECDSA using P-256 curve and SHA-256
299
- `ES384`: ECDSA using P-384 curve and SHA-384
300
- `ES512`: ECDSA using P-521 curve and SHA-512
301
302
```python
303
from jose.constants import ALGORITHMS
304
305
# HMAC signing (fastest, requires shared secret)
306
token = jws.sign(payload, 'shared-secret', algorithm=ALGORITHMS.HS256)
307
308
# RSA signing (widely supported, larger signatures)
309
token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS256)
310
311
# ECDSA signing (smaller signatures, newer standard)
312
token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES256)
313
```
314
315
## Error Handling
316
317
JWS operations can raise several specific exceptions:
318
319
```python { .api }
320
class JWSError(JOSEError):
321
"""Base exception for JWS-related errors."""
322
323
class JWSSignatureError(JWSError):
324
"""JWS signature verification failed."""
325
326
class JWSAlgorithmError(JWSError):
327
"""JWS algorithm not supported or invalid."""
328
```
329
330
**Error Handling Examples:**
331
332
```python
333
from jose.exceptions import JWSError, JWSSignatureError, JWSAlgorithmError
334
335
try:
336
payload = jws.verify(token, key, algorithms=['HS256'])
337
except JWSSignatureError:
338
print("Signature verification failed - invalid signature")
339
except JWSAlgorithmError:
340
print("Algorithm not supported or mismatch")
341
except JWSError as e:
342
print(f"JWS error: {e}")
343
344
# Signing errors
345
try:
346
token = jws.sign(payload, key, algorithm='UNSUPPORTED')
347
except JWSError as e:
348
print(f"Signing failed: {e}")
349
```
350
351
## Key Management Integration
352
353
JWS operations integrate with the JWK module for advanced key management:
354
355
```python
356
from jose import jws, jwk
357
358
# Construct key from various formats
359
key = jwk.construct(key_data, algorithm='HS256')
360
token = jws.sign(payload, key)
361
362
# Verify with constructed key
363
payload = jws.verify(token, key, algorithms=['HS256'])
364
365
# Multi-key scenarios
366
keys = [
367
jwk.construct(key1, 'HS256'),
368
jwk.construct(key2, 'HS256'),
369
jwk.construct(key3, 'HS256')
370
]
371
payload = jws.verify(token, keys, algorithms=['HS256'])
372
```
373
374
## Best Practices
375
376
1. **Always specify algorithms**: Never accept arbitrary algorithms in verification
377
2. **Use appropriate key sizes**: 256-bit keys for HS256, 2048+ bit RSA keys
378
3. **Handle verification errors**: Catch and handle signature verification failures
379
4. **Validate headers**: Check header parameters for expected values
380
5. **Key rotation**: Support multiple keys for seamless key rotation
381
6. **Algorithm selection**: Choose algorithms based on your security requirements:
382
- HMAC: Fast, requires shared secrets
383
- RSA: Widely supported, larger signatures
384
- ECDSA: Smaller signatures, modern standard