JOSE implementation in Python providing JWT, JWS, JWE, and JWK functionality with multiple cryptographic backends.
75
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.
Constructs JWK objects from various key formats and representations for use with JOSE operations.
def construct(key_data, algorithm=None):
"""
Constructs a JWK from key data.
Args:
key_data (str or bytes or dict): The key material in various formats:
- String secrets for HMAC algorithms
- PEM-formatted RSA/EC keys (public or private)
- SSH-formatted keys
- JWK dictionaries (JSON Web Key format)
- Raw key bytes
algorithm (str, optional): Algorithm hint for key construction.
Helps determine the appropriate key type when ambiguous
Returns:
Key: A key object (HMACKey, RSAKey, ECKey, AESKey, or DIRKey)
that can be used with jwt, jws, and jwe operations
Raises:
JWKError: If the key cannot be constructed or format is invalid
"""Usage Examples:
from jose import jwk
from jose.constants import ALGORITHMS
# HMAC key from string secret
key = jwk.construct('my-secret-key', algorithm=ALGORITHMS.HS256)
# HMAC key from bytes
key = jwk.construct(b'binary-secret-key', algorithm=ALGORITHMS.HS256)
# RSA key from PEM format
rsa_pem = """-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----"""
rsa_key = jwk.construct(rsa_pem, algorithm=ALGORITHMS.RS256)
# RSA public key
rsa_public_pem = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
rsa_public_key = jwk.construct(rsa_public_pem)
# EC key from PEM format
ec_pem = """-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----"""
ec_key = jwk.construct(ec_pem, algorithm=ALGORITHMS.ES256)
# JWK dictionary format
jwk_dict = {
'kty': 'oct', # Key type: octet sequence (symmetric)
'k': 'GawgguFyGrWKav7AX4VKUg', # base64url-encoded key value
'alg': 'HS256', # Algorithm
'use': 'sig' # Usage: signature
}
key = jwk.construct(jwk_dict)
# RSA JWK dictionary
rsa_jwk = {
'kty': 'RSA',
'n': 'public-modulus-base64url',
'd': 'private-exponent-base64url',
'e': 'AQAB', # public exponent (65537)
'alg': 'RS256'
}
rsa_key = jwk.construct(rsa_jwk)
# EC JWK dictionary
ec_jwk = {
'kty': 'EC',
'crv': 'P-256', # Curve
'x': 'x-coordinate-base64url',
'y': 'y-coordinate-base64url',
'd': 'private-key-base64url',
'alg': 'ES256'
}
ec_key = jwk.construct(ec_jwk)
# AES key for JWE
aes_key_bytes = b'This is a 32-byte key for AES256!!'
aes_key = jwk.construct(aes_key_bytes, algorithm=ALGORITHMS.A256GCM)Gets the appropriate key class for a given algorithm, useful for backend selection and key type determination.
def get_key(algorithm):
"""
Get key class for algorithm.
Args:
algorithm (str): The algorithm name (e.g., 'HS256', 'RS256', 'ES256')
Returns:
type or None: The key class that handles the algorithm, or None if not found
"""Usage Examples:
from jose import jwk
from jose.constants import ALGORITHMS
# Get key class for HMAC
hmac_key_class = jwk.get_key(ALGORITHMS.HS256)
print(hmac_key_class) # <class 'jose.backends.cryptography_backend.CryptographyHMACKey'>
# Get key class for RSA
rsa_key_class = jwk.get_key(ALGORITHMS.RS256)
print(rsa_key_class) # <class 'jose.backends.cryptography_backend.CryptographyRSAKey'>
# Get key class for EC
ec_key_class = jwk.get_key(ALGORITHMS.ES256)
print(ec_key_class) # <class 'jose.backends.cryptography_backend.CryptographyECKey'>
# Check if algorithm is supported
if jwk.get_key('UNSUPPORTED_ALG') is None:
print("Algorithm not supported")
# Dynamic key construction
algorithm = ALGORITHMS.HS256
key_class = jwk.get_key(algorithm)
if key_class:
key = key_class('secret-key', algorithm)Registers custom key classes for specific algorithms, enabling extensibility for custom cryptographic backends.
def register_key(algorithm, key_class):
"""
Registers a key class for an algorithm.
Args:
algorithm (str): The algorithm name to register
key_class (type): The key class that must inherit from jose.backends.base.Key
Returns:
bool: True if registration was successful
Raises:
JWKError: If the key class is invalid or doesn't inherit from Key
"""Usage Examples:
from jose import jwk
from jose.backends.base import Key
# Custom key implementation
class CustomHMACKey(Key):
def __init__(self, key_data, algorithm):
super().__init__(key_data, algorithm)
# Custom initialization
def sign(self, msg):
# Custom signing implementation
pass
def verify(self, msg, sig):
# Custom verification implementation
pass
# Register custom key
success = jwk.register_key('CUSTOM_HMAC', CustomHMACKey)
if success:
print("Custom key registered successfully")
# Use registered key
key = jwk.construct('secret', algorithm='CUSTOM_HMAC')The JWK module provides access to various key implementation classes through different cryptographic backends.
class Key:
"""
Base class for all key implementations.
Args:
prepared_key: The prepared key material
algorithm (str): The algorithm this key will be used with
"""
def __init__(self, prepared_key, algorithm):
"""Initialize the key with prepared key material and algorithm."""
def sign(self, msg):
"""
Sign a message (abstract method).
Args:
msg (bytes): The message to sign
Returns:
bytes: The signature
"""
def verify(self, msg, sig):
"""
Verify a signature (abstract method).
Args:
msg (bytes): The original message
sig (bytes): The signature to verify
Returns:
bool: True if signature is valid
"""
def encrypt(self, plaintext):
"""Encrypt plaintext (abstract method for JWE keys)."""
def decrypt(self, ciphertext, **kwargs):
"""Decrypt ciphertext (abstract method for JWE keys)."""
def wrap_key(self, key_data):
"""Wrap a key (abstract method for key wrapping algorithms)."""
def unwrap_key(self, wrapped_key):
"""Unwrap a key (abstract method for key wrapping algorithms)."""class HMACKey(Key):
"""
HMAC key implementation for symmetric signing algorithms.
Supports: HS256, HS384, HS512
"""
def __init__(self, key_data, algorithm):
"""Initialize HMAC key with secret and algorithm."""
def to_dict(self):
"""
Convert key to JWK dictionary format.
Returns:
dict: JWK representation of the key
"""
def sign(self, msg):
"""Sign message with HMAC."""
def verify(self, msg, sig):
"""Verify HMAC signature."""class RSAKey(Key):
"""
RSA key implementation for asymmetric operations.
Supports: RS256, RS384, RS512, PS256, PS384, PS512 (backend dependent)
Key wrapping: RSA1_5, RSA-OAEP, RSA-OAEP-256
"""
def __init__(self, key_data, algorithm):
"""Initialize RSA key from PEM data or JWK dictionary."""
def to_dict(self):
"""Convert to JWK dictionary format."""
def sign(self, msg):
"""Sign message with RSA private key."""
def verify(self, msg, sig):
"""Verify signature with RSA public key."""
def encrypt(self, plaintext):
"""Encrypt with RSA public key (for JWE)."""
def decrypt(self, ciphertext):
"""Decrypt with RSA private key (for JWE)."""class ECKey(Key):
"""
Elliptic Curve key implementation.
Supports: ES256, ES384, ES512
Curves: P-256, P-384, P-521
"""
def __init__(self, key_data, algorithm):
"""Initialize EC key from PEM data or JWK dictionary."""
def to_dict(self):
"""Convert to JWK dictionary format."""
def sign(self, msg):
"""Sign message with EC private key."""
def verify(self, msg, sig):
"""Verify signature with EC public key."""class AESKey(Key):
"""
AES key implementation for symmetric encryption.
Supports: A128KW, A192KW, A256KW (key wrapping)
A128GCM, A192GCM, A256GCM (content encryption)
"""
def __init__(self, key_data, algorithm):
"""Initialize AES key with key bytes."""
def encrypt(self, plaintext):
"""Encrypt with AES (algorithm-dependent mode)."""
def decrypt(self, ciphertext, **kwargs):
"""Decrypt with AES (algorithm-dependent mode)."""
def wrap_key(self, key_data):
"""Wrap key with AES key wrapping."""
def unwrap_key(self, wrapped_key):
"""Unwrap key with AES key wrapping."""JWK (JSON Web Key) provides a standardized JSON representation for cryptographic keys:
# HMAC key JWK format
hmac_jwk = {
'kty': 'oct', # Key type: octet sequence
'k': 'base64url-key', # Key value (base64url encoded)
'alg': 'HS256', # Algorithm (optional)
'use': 'sig', # Usage: sig (signature) or enc (encryption)
'kid': 'key-id' # Key ID (optional)
}# RSA private key JWK format
rsa_private_jwk = {
'kty': 'RSA', # Key type: RSA
'n': 'modulus-b64url', # Public modulus
'e': 'AQAB', # Public exponent (usually 65537)
'd': 'private-exp-b64url', # Private exponent
'p': 'prime1-b64url', # First prime factor
'q': 'prime2-b64url', # Second prime factor
'dp': 'exp1-b64url', # First factor exponent
'dq': 'exp2-b64url', # Second factor exponent
'qi': 'coefficient-b64url', # Coefficient
'alg': 'RS256', # Algorithm
'use': 'sig', # Usage
'kid': 'rsa-key-1' # Key ID
}
# RSA public key JWK format (subset of private)
rsa_public_jwk = {
'kty': 'RSA',
'n': 'modulus-b64url',
'e': 'AQAB',
'alg': 'RS256',
'use': 'sig',
'kid': 'rsa-key-1'
}# EC private key JWK format
ec_private_jwk = {
'kty': 'EC', # Key type: Elliptic Curve
'crv': 'P-256', # Curve: P-256, P-384, or P-521
'x': 'x-coord-b64url', # X coordinate
'y': 'y-coord-b64url', # Y coordinate
'd': 'private-key-b64url', # Private key
'alg': 'ES256', # Algorithm
'use': 'sig', # Usage
'kid': 'ec-key-1' # Key ID
}
# EC public key JWK format (without 'd')
ec_public_jwk = {
'kty': 'EC',
'crv': 'P-256',
'x': 'x-coord-b64url',
'y': 'y-coord-b64url',
'alg': 'ES256',
'use': 'sig',
'kid': 'ec-key-1'
}The JWK module supports various input key formats:
# RSA private key in PEM format
rsa_pem = """-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----"""
# RSA public key in PEM format
rsa_public_pem = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
# EC private key in PEM format
ec_pem = """-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----"""
# Construct keys from PEM
rsa_key = jwk.construct(rsa_pem)
ec_key = jwk.construct(ec_pem)# HMAC secret as string or bytes
hmac_secret = 'my-secret-key'
hmac_bytes = b'binary-secret-key'
# AES key as bytes (specific lengths)
aes_128_key = b'16-byte key here'
aes_256_key = b'This is a 32-byte key for AES256!!'
# Construct from raw material
hmac_key = jwk.construct(hmac_secret, algorithm='HS256')
aes_key = jwk.construct(aes_256_key, algorithm='A256GCM')JWK automatically selects the best available cryptographic backend:
# Backend priority (highest to lowest):
# 1. cryptography (pyca/cryptography) - Recommended
# 2. native-python (python-rsa, python-ecdsa)
# 3. pycryptodome (alternative backend)
# Check which backend is being used
key = jwk.construct('secret', algorithm='HS256')
print(type(key)) # Shows the backend class being used
# Examples of backend classes:
# - CryptographyHMACKey (cryptography backend)
# - CryptographyRSAKey (cryptography backend)
# - CryptographyECKey (cryptography backend)
# - RSAKey (native backend)
# - ECDSAECKey (native backend)JWK operations can raise specific exceptions:
class JWKError(JOSEError):
"""Base exception for JWK-related errors."""Error Handling Examples:
from jose.exceptions import JWKError
try:
# Invalid key format
key = jwk.construct('invalid-key-format')
except JWKError as e:
print(f"Key construction failed: {e}")
try:
# Unsupported algorithm
key_class = jwk.get_key('UNKNOWN_ALGORITHM')
if key_class is None:
print("Algorithm not supported")
except JWKError as e:
print(f"Key retrieval failed: {e}")
try:
# Invalid JWK dictionary
invalid_jwk = {'kty': 'INVALID', 'k': 'key'}
key = jwk.construct(invalid_jwk)
except JWKError as e:
print(f"Invalid JWK: {e}")JWK objects integrate seamlessly with JWT, JWS, and JWE operations:
from jose import jwt, jws, jwe, jwk
# Construct key once, use everywhere
key = jwk.construct('shared-secret', algorithm='HS256')
# Use with JWT
token = jwt.encode({'user': 'john'}, key)
claims = jwt.decode(token, key, algorithms=['HS256'])
# Use with JWS
signature = jws.sign('message', key)
payload = jws.verify(signature, key, algorithms=['HS256'])
# For JWE (with appropriate key type)
aes_key = jwk.construct(b'32-byte-key-for-aes-256-gcm!!!!', algorithm='A256GCM')
encrypted = jwe.encrypt(b'secret', aes_key, algorithm='dir')
plaintext = jwe.decrypt(encrypted, aes_key)Install with Tessl CLI
npx tessl i tessl/pypi-python-jose