JOSE implementation in Python providing JWT, JWS, JWE, and JWK functionality with multiple cryptographic backends.
75
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.
Signs arbitrary payloads using various algorithms and returns JWS compact serialization format.
def sign(payload, key, headers=None, algorithm='HS256'):
"""
Signs a payload and returns a JWS string.
Args:
payload (str or dict): A payload to sign. Can be:
- String data to sign directly
- Dictionary that will be JSON-encoded before signing
key (str or bytes or dict): The key to use for signing. Supports:
- String secrets for HMAC algorithms
- RSA/EC private keys in PEM format
- JWK dictionaries
- Key objects from jose.jwk
headers (dict, optional): Additional headers to include in the JWS header.
Will be merged with default headers (alg, typ)
algorithm (str): The algorithm to use for signing. Defaults to 'HS256'.
Supported: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
Returns:
str: The JWS token in compact format (header.payload.signature)
Raises:
JWSError: If there is an error signing the payload or algorithm not supported
"""Usage Examples:
from jose import jws
from jose.constants import ALGORITHMS
# Sign a string payload with HMAC
payload = "Hello, World!"
token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256)
# Sign a dictionary payload (auto JSON-encoded)
payload = {'message': 'Hello', 'timestamp': 1234567890}
token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256)
# Sign with additional headers
headers = {'kid': 'key-1', 'cty': 'text/plain'}
token = jws.sign("Important message", 'secret', headers=headers)
# RSA signing with private key
rsa_private_key = """-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----"""
token = jws.sign("Secure message", rsa_private_key, algorithm=ALGORITHMS.RS256)
# ECDSA signing
ec_private_key = """-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----"""
token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES256)
# Using JWK dictionary
jwk_key = {
'kty': 'oct',
'k': 'GawgguFyGrWKav7AX4VKUg' # base64url-encoded secret
}
token = jws.sign(payload, jwk_key, algorithm=ALGORITHMS.HS256)Verifies JWS signatures and returns the original payload after successful verification.
def verify(token, key, algorithms, verify=True):
"""
Verifies a JWS token and returns the payload.
Args:
token (str or bytes): The JWS token to verify in compact format
key (str or bytes or dict or list): The verification key(s). Supports:
- String secrets for HMAC algorithms
- RSA/EC public keys in PEM format
- JWK dictionaries
- List of keys to try multiple verification keys
- Key objects from jose.jwk
algorithms (str or list): Allowed algorithms for verification. Required
for security - specify exactly which algorithms you accept
verify (bool): Whether to verify the signature. Defaults to True.
Set to False only for debugging or when signature is verified elsewhere
Returns:
bytes: The verified payload as bytes
Raises:
JWSError: If verification fails or token format is invalid
JWSSignatureError: If signature verification specifically fails
"""Usage Examples:
from jose import jws
from jose.exceptions import JWSError, JWSSignatureError
try:
# Basic HMAC verification
payload = jws.verify(token, 'secret', algorithms=['HS256'])
print(payload.decode('utf-8')) # Convert bytes to string
except JWSSignatureError:
print("Signature verification failed")
except JWSError as e:
print(f"JWS verification error: {e}")
# RSA public key verification
rsa_public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
payload = jws.verify(token, rsa_public_key, algorithms=['RS256'])
# Multiple algorithm support
payload = jws.verify(token, key, algorithms=['HS256', 'HS384', 'HS512'])
# Multiple key verification (try different keys)
keys = [
'primary-secret',
'backup-secret',
{'kty': 'oct', 'k': 'encoded-key'}
]
payload = jws.verify(token, keys, algorithms=['HS256'])
# Skip signature verification (debugging only)
payload = jws.verify(token, '', algorithms=['HS256'], verify=False)
# EC public key verification
ec_public_key = """-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----"""
payload = jws.verify(token, ec_public_key, algorithms=['ES256'])Retrieve JWS headers and payload without performing signature verification.
def get_unverified_header(token):
"""
Get JWS header without verification.
Args:
token (str or bytes): The JWS token
Returns:
dict: The JWS header dictionary
Raises:
JWSError: If the token format is invalid
"""
def get_unverified_headers(token):
"""
Get JWS header without verification (alias for get_unverified_header).
Args:
token (str or bytes): The JWS token
Returns:
dict: The JWS header dictionary
Raises:
JWSError: If the token format is invalid
"""
def get_unverified_claims(token):
"""
Get JWS payload without verification.
Args:
token (str or bytes): The JWS token
Returns:
bytes: The JWS payload as bytes
Raises:
JWSError: If the token format is invalid
"""Usage Examples:
# Inspect JWS header
header = jws.get_unverified_header(token)
print(f"Algorithm: {header['alg']}")
print(f"Key ID: {header.get('kid')}")
print(f"Content Type: {header.get('cty')}")
# Get payload without verification
payload_bytes = jws.get_unverified_claims(token)
payload_str = payload_bytes.decode('utf-8')
print(f"Payload: {payload_str}")
# Conditional verification based on algorithm
header = jws.get_unverified_header(token)
if header['alg'] in ['HS256', 'HS384', 'HS512']:
payload = jws.verify(token, hmac_secret, algorithms=[header['alg']])
elif header['alg'] in ['RS256', 'RS384', 'RS512']:
payload = jws.verify(token, rsa_public_key, algorithms=[header['alg']])
elif header['alg'] in ['ES256', 'ES384', 'ES512']:
payload = jws.verify(token, ec_public_key, algorithms=[header['alg']])JWS headers contain metadata about the signature algorithm and optional additional information:
# Standard JWS header fields
header = {
'alg': 'HS256', # Algorithm (required)
'typ': 'JWT', # Type (optional, commonly 'JWT')
'kid': 'key-identifier', # Key ID for key selection (optional)
'cty': 'application/json', # Content type (optional)
'crit': ['custom-claim'] # Critical header parameters (optional)
}Header Examples:
# Minimal header (algorithm only)
headers = {'alg': 'HS256'}
# Header with key identification
headers = {'alg': 'RS256', 'kid': 'rsa-key-1'}
# Header with content type for non-JSON payloads
headers = {'alg': 'HS256', 'cty': 'text/plain'}
# Custom headers (use with caution)
headers = {
'alg': 'HS256',
'custom': 'value',
'x-app-version': '1.0.0'
}JWS can sign various payload formats:
# String payloads
payload = "Plain text message"
token = jws.sign(payload, key)
# JSON payloads (dictionaries)
payload = {'user': 'john', 'action': 'login'}
token = jws.sign(payload, key) # Auto-converted to JSON
# Binary payloads
payload = b"Binary data\x00\x01\x02"
token = jws.sign(payload, key)
# Pre-encoded JSON strings
import json
payload = json.dumps({'data': 'value'})
token = jws.sign(payload, key)JWS supports multiple signature algorithms:
HMAC Algorithms (Symmetric):
HS256: HMAC using SHA-256 hashHS384: HMAC using SHA-384 hashHS512: HMAC using SHA-512 hashRSA Algorithms (Asymmetric):
RS256: RSA PKCS#1 v1.5 using SHA-256RS384: RSA PKCS#1 v1.5 using SHA-384RS512: RSA PKCS#1 v1.5 using SHA-512ECDSA Algorithms (Asymmetric):
ES256: ECDSA using P-256 curve and SHA-256ES384: ECDSA using P-384 curve and SHA-384ES512: ECDSA using P-521 curve and SHA-512from jose.constants import ALGORITHMS
# HMAC signing (fastest, requires shared secret)
token = jws.sign(payload, 'shared-secret', algorithm=ALGORITHMS.HS256)
# RSA signing (widely supported, larger signatures)
token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS256)
# ECDSA signing (smaller signatures, newer standard)
token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES256)JWS operations can raise several specific exceptions:
class JWSError(JOSEError):
"""Base exception for JWS-related errors."""
class JWSSignatureError(JWSError):
"""JWS signature verification failed."""
class JWSAlgorithmError(JWSError):
"""JWS algorithm not supported or invalid."""Error Handling Examples:
from jose.exceptions import JWSError, JWSSignatureError, JWSAlgorithmError
try:
payload = jws.verify(token, key, algorithms=['HS256'])
except JWSSignatureError:
print("Signature verification failed - invalid signature")
except JWSAlgorithmError:
print("Algorithm not supported or mismatch")
except JWSError as e:
print(f"JWS error: {e}")
# Signing errors
try:
token = jws.sign(payload, key, algorithm='UNSUPPORTED')
except JWSError as e:
print(f"Signing failed: {e}")JWS operations integrate with the JWK module for advanced key management:
from jose import jws, jwk
# Construct key from various formats
key = jwk.construct(key_data, algorithm='HS256')
token = jws.sign(payload, key)
# Verify with constructed key
payload = jws.verify(token, key, algorithms=['HS256'])
# Multi-key scenarios
keys = [
jwk.construct(key1, 'HS256'),
jwk.construct(key2, 'HS256'),
jwk.construct(key3, 'HS256')
]
payload = jws.verify(token, keys, algorithms=['HS256'])Install with Tessl CLI
npx tessl i tessl/pypi-python-jose