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

jws-operations.mddocs/

JWS Operations

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.

Capabilities

Payload Signing

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)

Signature Verification

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'])

Header and Payload Inspection

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 Header Structure

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'
}

Payload Formats

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)

Algorithm Support

JWS supports multiple signature algorithms:

HMAC Algorithms (Symmetric):

  • HS256: HMAC using SHA-256 hash
  • HS384: HMAC using SHA-384 hash
  • HS512: HMAC using SHA-512 hash

RSA Algorithms (Asymmetric):

  • RS256: RSA PKCS#1 v1.5 using SHA-256
  • RS384: RSA PKCS#1 v1.5 using SHA-384
  • RS512: RSA PKCS#1 v1.5 using SHA-512

ECDSA Algorithms (Asymmetric):

  • ES256: ECDSA using P-256 curve and SHA-256
  • ES384: ECDSA using P-384 curve and SHA-384
  • ES512: ECDSA using P-521 curve and SHA-512
from 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)

Error Handling

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}")

Key Management Integration

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'])

Best Practices

  1. Always specify algorithms: Never accept arbitrary algorithms in verification
  2. Use appropriate key sizes: 256-bit keys for HS256, 2048+ bit RSA keys
  3. Handle verification errors: Catch and handle signature verification failures
  4. Validate headers: Check header parameters for expected values
  5. Key rotation: Support multiple keys for seamless key rotation
  6. Algorithm selection: Choose algorithms based on your security requirements:
    • HMAC: Fast, requires shared secrets
    • RSA: Widely supported, larger signatures
    • ECDSA: Smaller signatures, modern standard

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