CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-python-jose

JOSE implementation in Python providing JWT, JWS, JWE, and JWK functionality with multiple cryptographic backends.

75

1.44x
Overview
Eval results
Files

jwe-operations.mddocs/

JWE Operations

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.

Capabilities

Content Encryption

Encrypts plaintext content and returns JWE compact serialization format with support for various encryption algorithms and key management methods.

def encrypt(plaintext, key, encryption='A256GCM', algorithm='dir', zip=None, cty=None, kid=None):
    """
    Encrypts plaintext and returns a JWE compact serialization string.
    
    Args:
        plaintext (bytes): The data to encrypt. Must be bytes object.
            Use encode() to convert strings to bytes
        key (str or bytes or dict): The encryption key. Supports:
            - Bytes for direct encryption (dir algorithm)
            - RSA public keys in PEM format for RSA key wrapping
            - JWK dictionaries with appropriate key material
        encryption (str): The content encryption algorithm. Defaults to 'A256GCM'.
            Supported: A128GCM, A192GCM, A256GCM, A128CBC-HS256, A192CBC-HS384, A256CBC-HS512
        algorithm (str): The key management algorithm. Defaults to 'dir'.
            Supported: dir, RSA1_5, RSA-OAEP, RSA-OAEP-256, A128KW, A192KW, A256KW
        zip (str, optional): The compression algorithm applied before encryption.
            Supported: 'DEF' for DEFLATE compression, None for no compression
        cty (str, optional): The content type of the secured content.
            Examples: 'application/json', 'text/plain'
        kid (str, optional): Key ID hint for the recipient to identify the key
    
    Returns:
        bytes: The JWE token in compact format (header.encryptedkey.iv.ciphertext.tag)
    
    Raises:
        JWEError: If encryption fails or parameters are invalid
        JWEAlgorithmUnsupportedError: If the specified algorithm is not supported
    """

Usage Examples:

from jose import jwe
from jose.constants import ALGORITHMS

# Basic AES-GCM encryption with direct key
key = b'This is a 32-byte key for AES256!!'  # 32 bytes for A256GCM
plaintext = b'Secret message'
encrypted = jwe.encrypt(plaintext, key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)

# String to bytes conversion
message = "Confidential data"
encrypted = jwe.encrypt(message.encode('utf-8'), key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)

# RSA key wrapping with OAEP
rsa_public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""

encrypted = jwe.encrypt(
    b'Sensitive data', 
    rsa_public_key, 
    encryption=ALGORITHMS.A256GCM,
    algorithm=ALGORITHMS.RSA_OAEP
)

# With compression
encrypted = jwe.encrypt(
    b'Large repetitive data that compresses well...',
    key,
    encryption=ALGORITHMS.A256GCM,
    algorithm=ALGORITHMS.DIR,
    zip='DEF'  # DEFLATE compression
)

# With content type and key ID
encrypted = jwe.encrypt(
    b'{"user": "john", "role": "admin"}',
    key,
    encryption=ALGORITHMS.A256GCM,
    algorithm=ALGORITHMS.DIR,
    cty='application/json',
    kid='encryption-key-1'
)

# AES key wrapping
aes_kek = b'Key Encryption Key 32 bytes!!!!!'  # 32 bytes for A256KW
encrypted = jwe.encrypt(
    b'Protected content',
    aes_kek,
    encryption=ALGORITHMS.A256GCM,
    algorithm=ALGORITHMS.A256KW
)

# CBC with HMAC authentication
encrypted = jwe.encrypt(
    b'Legacy compatibility data',
    key,
    encryption=ALGORITHMS.A256CBC_HS512,
    algorithm=ALGORITHMS.DIR
)

Content Decryption

Decrypts JWE tokens and returns the original plaintext after verifying authentication.

def decrypt(jwe_str, key):
    """
    Decrypts a JWE token and returns the original plaintext.
    
    Args:
        jwe_str (str or bytes): The JWE token to decrypt in compact format
        key (str or bytes or dict): The decryption key. Must match the key
            used for encryption and be appropriate for the key management algorithm:
            - Bytes for direct encryption (dir algorithm)  
            - RSA private keys in PEM format for RSA key wrapping
            - AES keys for AES key wrapping
            - JWK dictionaries with appropriate key material
    
    Returns:
        bytes: The decrypted plaintext as bytes. Use decode() to convert to string
    
    Raises:
        JWEError: If decryption fails or token format is invalid
        JWEParseError: If the JWE token cannot be parsed
        JWEInvalidAuth: If authentication verification fails
    """

Usage Examples:

from jose import jwe
from jose.exceptions import JWEError, JWEParseError, JWEInvalidAuth

try:
    # Basic decryption
    plaintext = jwe.decrypt(encrypted_token, key)
    message = plaintext.decode('utf-8')
    print(f"Decrypted: {message}")
    
except JWEInvalidAuth:
    print("Authentication verification failed - data may be tampered")
except JWEParseError:
    print("Invalid JWE token format")
except JWEError as e:
    print(f"Decryption failed: {e}")

# RSA key unwrapping
rsa_private_key = """-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----"""

plaintext = jwe.decrypt(encrypted_token, rsa_private_key)

# AES key unwrapping
aes_kek = b'Key Encryption Key 32 bytes!!!!!'
plaintext = jwe.decrypt(encrypted_token, aes_kek)

# Direct key decryption
direct_key = b'This is a 32-byte key for AES256!!'
plaintext = jwe.decrypt(encrypted_token, direct_key)

# JWK dictionary key
jwk_key = {
    'kty': 'oct',
    'k': 'VGhpcyBpcyBhIDMyLWJ5dGUga2V5IGZvciBBRVMyNTYhIQ'  # base64url encoded
}
plaintext = jwe.decrypt(encrypted_token, jwk_key)

Header Inspection

Retrieve JWE header information without performing decryption.

def get_unverified_header(jwe_str):
    """
    Get JWE header without verification or decryption.
    
    Args:
        jwe_str (str or bytes): The JWE token
    
    Returns:
        dict: The JWE header dictionary
    
    Raises:
        JWEParseError: If the JWE token format is invalid
    """

Usage Examples:

# Inspect JWE header
header = jwe.get_unverified_header(encrypted_token)
print(f"Key Management Algorithm: {header['alg']}")
print(f"Content Encryption Algorithm: {header['enc']}")
print(f"Key ID: {header.get('kid')}")
print(f"Content Type: {header.get('cty')}")
print(f"Compression: {header.get('zip')}")

# Key selection based on header
header = jwe.get_unverified_header(encrypted_token)
key_id = header.get('kid')
if key_id == 'rsa-key-1':
    key = rsa_private_key_1
elif key_id == 'rsa-key-2':
    key = rsa_private_key_2
else:
    key = default_key

plaintext = jwe.decrypt(encrypted_token, key)

JWE Header Structure

JWE headers contain metadata about encryption algorithms and key management:

# Standard JWE header fields
header = {
    'alg': 'dir',             # Key management algorithm (required)
    'enc': 'A256GCM',         # Content encryption algorithm (required)
    'zip': 'DEF',             # Compression algorithm (optional)
    'kid': 'key-1',           # Key ID for key identification (optional)
    'cty': 'application/json', # Content type (optional)
    'typ': 'JWE'              # Type (optional)
}

Encryption Algorithms

JWE supports multiple content encryption algorithms:

AES-GCM (Authenticated Encryption):

  • A128GCM: AES-128-GCM (16-byte key)
  • A192GCM: AES-192-GCM (24-byte key)
  • A256GCM: AES-256-GCM (32-byte key) - Recommended

AES-CBC with HMAC (Legacy Compatibility):

  • A128CBC-HS256: AES-128-CBC + HMAC-SHA-256
  • A192CBC-HS384: AES-192-CBC + HMAC-SHA-384
  • A256CBC-HS512: AES-256-CBC + HMAC-SHA-512
from jose.constants import ALGORITHMS

# Modern authenticated encryption (recommended)
encrypted = jwe.encrypt(data, key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)

# Legacy compatibility
encrypted = jwe.encrypt(data, key, ALGORITHMS.A256CBC_HS512, ALGORITHMS.DIR)

# Different key sizes
encrypted = jwe.encrypt(data, key_16, ALGORITHMS.A128GCM, ALGORITHMS.DIR)  # 16-byte key
encrypted = jwe.encrypt(data, key_24, ALGORITHMS.A192GCM, ALGORITHMS.DIR)  # 24-byte key
encrypted = jwe.encrypt(data, key_32, ALGORITHMS.A256GCM, ALGORITHMS.DIR)  # 32-byte key

Key Management Algorithms

JWE supports various key management methods:

Direct Encryption:

  • dir: Direct use of Content Encryption Key (CEK)

RSA Key Wrapping:

  • RSA1_5: RSA PKCS#1 v1.5 (legacy, not recommended)
  • RSA-OAEP: RSA OAEP with SHA-1 and MGF1
  • RSA-OAEP-256: RSA OAEP with SHA-256 and MGF1 - Recommended

AES Key Wrapping:

  • A128KW: AES-128 Key Wrap
  • A192KW: AES-192 Key Wrap
  • A256KW: AES-256 Key Wrap
# Direct encryption (CEK = key)
encrypted = jwe.encrypt(plaintext, direct_key, algorithm=ALGORITHMS.DIR)

# RSA key wrapping (recommended OAEP-256)
encrypted = jwe.encrypt(plaintext, rsa_public_key, algorithm=ALGORITHMS.RSA_OAEP_256)

# AES key wrapping
encrypted = jwe.encrypt(plaintext, aes_kek, algorithm=ALGORITHMS.A256KW)

Key Requirements

Different algorithms require specific key formats and sizes:

# Direct encryption key requirements
key_128 = b'16-byte key here'    # For A128GCM
key_192 = b'24-byte key here!!!!!!!!'  # For A192GCM  
key_256 = b'This is a 32-byte key for AES256!!'  # For A256GCM

# RSA keys (PEM format)
rsa_public_key = """-----BEGIN PUBLIC KEY-----
... (RSA public key in PEM format for encryption)
-----END PUBLIC KEY-----"""

rsa_private_key = """-----BEGIN PRIVATE KEY-----
... (RSA private key in PEM format for decryption)  
-----END PRIVATE KEY-----"""

# AES Key Encryption Keys (KEK)
aes_kek_128 = b'16-byte KEK here'           # For A128KW
aes_kek_192 = b'24-byte KEK here!!!!!!!'   # For A192KW
aes_kek_256 = b'32-byte KEK for wrapping!!!!!!!!'  # For A256KW

Compression Support

JWE supports DEFLATE compression to reduce ciphertext size:

# With compression (useful for large or repetitive data)
large_data = b'{"users": [' + b'{"name": "user", "role": "admin"},' * 1000 + b']}'
encrypted = jwe.encrypt(
    large_data,
    key,
    encryption=ALGORITHMS.A256GCM,
    algorithm=ALGORITHMS.DIR,
    zip='DEF'  # DEFLATE compression
)

# Compression is transparent during decryption
decrypted = jwe.decrypt(encrypted, key)  # Automatically decompressed

Error Handling

JWE operations can raise several specific exceptions:

class JWEError(JOSEError):
    """Base exception for JWE-related errors."""

class JWEParseError(JWEError):
    """JWE token parsing failed."""

class JWEInvalidAuth(JWEError):
    """JWE authentication verification failed."""

class JWEAlgorithmUnsupportedError(JWEError):
    """JWE algorithm not supported."""

Error Handling Examples:

from jose.exceptions import JWEError, JWEParseError, JWEInvalidAuth, JWEAlgorithmUnsupportedError

try:
    plaintext = jwe.decrypt(encrypted_token, key)
except JWEInvalidAuth:
    print("Authentication failed - data may be corrupted or tampered")
except JWEParseError:
    print("Invalid JWE token format")
except JWEAlgorithmUnsupportedError:
    print("Encryption algorithm not supported by current backend")
except JWEError as e:
    print(f"JWE operation failed: {e}")

# Encryption errors
try:
    encrypted = jwe.encrypt(plaintext, key, 'UNSUPPORTED_ALG', 'dir')
except JWEAlgorithmUnsupportedError:
    print("Unsupported encryption algorithm")
except JWEError as e:
    print(f"Encryption failed: {e}")

Security Considerations

  1. Algorithm Selection: Use A256GCM with RSA-OAEP-256 for new applications
  2. Key Management: Generate cryptographically secure random keys
  3. Key Size: Use appropriate key sizes (256-bit for AES, 2048+ bit for RSA)
  4. Authentication: Always verify authentication (handled automatically)
  5. Key Storage: Store encryption keys securely, separate from encrypted data
  6. Key Rotation: Implement key rotation for long-term security

Integration with Other Modules

JWE works with other jose modules for complete JOSE functionality:

from jose import jwe, jwk

# Use JWK for key management
key = jwk.construct({'kty': 'oct', 'k': 'encoded-key'})
encrypted = jwe.encrypt(plaintext, key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)

# Nested JWT (signed then encrypted)
from jose import jwt
# First sign with JWT
signed_token = jwt.encode(claims, signing_key, algorithm='HS256')
# Then encrypt the JWT
encrypted_jwt = jwe.encrypt(signed_token.encode(), encryption_key, ALGORITHMS.A256GCM, ALGORITHMS.DIR)

# Decrypt then verify
decrypted_jwt = jwe.decrypt(encrypted_jwt, encryption_key)
claims = jwt.decode(decrypted_jwt.decode(), signing_key, algorithms=['HS256'])

Best Practices

  1. Use authenticated encryption: Prefer A256GCM over CBC+HMAC modes
  2. Strong key management: Use RSA-OAEP-256 or secure key distribution
  3. Key rotation: Implement regular key rotation policies
  4. Secure random keys: Generate keys using cryptographically secure random sources
  5. Validate inputs: Always validate plaintext size (max 250KB) and key formats
  6. Handle errors gracefully: Implement proper error handling for all failure modes
  7. Compression consideration: Use compression only when beneficial for data size

Install with Tessl CLI

npx tessl i tessl/pypi-python-jose

docs

constants-algorithms.md

index.md

jwe-operations.md

jwk-management.md

jws-operations.md

jwt-operations.md

tile.json