CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-josepy

JOSE protocol implementation in Python with support for JSON Web Algorithms, Keys, and Signatures

73

1.15x
Overview
Eval results
Files

jwk.mddocs/

JSON Web Keys (JWK)

Key representation and management implementing the JSON Web Key specification (RFC 7517). Provides unified interfaces for RSA, Elliptic Curve, and symmetric keys with support for key loading, thumbprint generation, and public key extraction.

Capabilities

Base JWK Class

Abstract base class providing common functionality for all JSON Web Key types.

class JWK:
    """Base class for JSON Web Keys"""
    
    def thumbprint(self, hash_function=hashes.SHA256) -> bytes:
        """
        Compute JWK Thumbprint according to RFC 7638.
        
        Parameters:
        - hash_function: Hash algorithm factory (default: SHA256)
        
        Returns:
        bytes: Key thumbprint hash
        """
    
    def public_key(self) -> 'JWK':
        """
        Generate JWK containing only the public key.
        For symmetric keys, returns self.
        
        Returns:
        JWK: Public key JWK instance
        """
    
    @classmethod
    def from_json(cls, jobj: Any) -> 'JWK':
        """Deserialize JWK from JSON object"""
    
    def to_partial_json(self) -> Any:
        """Serialize JWK to JSON-compatible dictionary"""
    
    @classmethod
    def load(cls, data: bytes, password: Optional[bytes] = None, backend: Optional[Any] = None) -> 'JWK':
        """
        Load serialized key as JWK with automatic type detection.
        
        Parameters:
        - data: Public or private key serialized as PEM or DER
        - password: Optional password for encrypted private keys
        - backend: Cryptography backend (defaults to default_backend())
        
        Returns:
        JWK: JWK instance of appropriate type (JWKRSA, JWKEC, or JWKOct)
        
        Raises:
        josepy.errors.Error: If unable to deserialize or unsupported algorithm
        """
    
    @classmethod
    def _load_cryptography_key(cls, data: bytes, password: Optional[bytes] = None, backend: Optional[Any] = None) -> Any:
        """Load cryptography key from PEM/DER data"""
    
    key: Any  # Underlying cryptography key object

RSA Keys (JWKRSA)

RSA key representation supporting both private and public keys with PKCS#1 and PSS algorithms.

class JWKRSA(JWK):
    """RSA JSON Web Key"""
    
    def __init__(self, key=None):
        """
        Initialize RSA JWK.
        
        Parameters:
        - key: RSA private or public key from cryptography library,
               or bytes/str containing PEM/DER encoded key data
        """
    
    @classmethod
    def load(cls, data: bytes, password: Optional[bytes] = None) -> 'JWKRSA':
        """
        Load RSA key from PEM or DER encoded data.
        
        Parameters:
        - data: PEM or DER encoded key data
        - password: Password for encrypted private keys
        
        Returns:
        JWKRSA: RSA JWK instance
        """
    
    def public_key(self) -> 'JWKRSA':
        """Extract public key component"""
    
    # JSON field mappings for RSA parameters
    n: bytes  # Modulus
    e: bytes  # Public exponent
    d: bytes  # Private exponent (private keys only)
    p: bytes  # First prime factor (private keys only)
    q: bytes  # Second prime factor (private keys only)
    dp: bytes  # First factor CRT exponent (private keys only)
    dq: bytes  # Second factor CRT exponent (private keys only)
    qi: bytes  # First CRT coefficient (private keys only)

Usage Examples

from josepy import JWKRSA
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

# Generate new RSA key
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

# Create JWK from cryptography key
jwk = JWKRSA(key=private_key)

# Get public key JWK
public_jwk = jwk.public_key()

# Compute thumbprint
thumbprint = jwk.thumbprint()
print(f"Key thumbprint: {thumbprint.hex()}")

# Serialize to JSON
jwk_json = jwk.json_dumps()
print(f"JWK JSON: {jwk_json}")

# Load from JSON
loaded_jwk = JWKRSA.json_loads(jwk_json)

# Load from PEM data
pem_data = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)
jwk_from_pem = JWKRSA.load(pem_data)

Elliptic Curve Keys (JWKEC)

Elliptic Curve key representation supporting NIST P-curves for ECDSA operations.

class JWKEC(JWK):
    """Elliptic Curve JSON Web Key"""
    
    def __init__(self, key=None):
        """
        Initialize EC JWK.
        
        Parameters:
        - key: EC private or public key from cryptography library,
               or bytes/str containing PEM/DER encoded key data
        """
    
    @classmethod
    def load(cls, data: bytes, password: Optional[bytes] = None) -> 'JWKEC':
        """
        Load EC key from PEM or DER encoded data.
        
        Parameters:
        - data: PEM or DER encoded key data
        - password: Password for encrypted private keys
        
        Returns:
        JWKEC: EC JWK instance
        """
    
    def public_key(self) -> 'JWKEC':
        """Extract public key component"""
    
    # JSON field mappings for EC parameters
    crv: str  # Curve name ("P-256", "P-384", "P-521")
    x: bytes  # X coordinate
    y: bytes  # Y coordinate
    d: bytes  # Private key value (private keys only)

Usage Examples

from josepy import JWKEC
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend

# Generate new EC key (P-256)
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())

# Create JWK from cryptography key
jwk_ec = JWKEC(key=private_key)

# Get public key JWK
public_jwk_ec = jwk_ec.public_key()

# Compute thumbprint
thumbprint_ec = jwk_ec.thumbprint()
print(f"EC Key thumbprint: {thumbprint_ec.hex()}")

# Different curves
p384_key = ec.generate_private_key(ec.SECP384R1(), default_backend())
jwk_p384 = JWKEC(key=p384_key)

p521_key = ec.generate_private_key(ec.SECP521R1(), default_backend())
jwk_p521 = JWKEC(key=p521_key)

Symmetric Keys (JWKOct)

Octet string key representation for symmetric cryptographic operations including HMAC.

class JWKOct(JWK):
    """Symmetric (Octet String) JSON Web Key"""
    
    def __init__(self, key=None):
        """
        Initialize symmetric JWK.
        
        Parameters:
        - key: bytes containing the symmetric key material
        """
    
    def public_key(self) -> 'JWKOct':
        """For symmetric keys, returns self"""
    
    # JSON field mappings
    k: bytes  # Key value (base64url encoded in JSON)

Usage Examples

from josepy import JWKOct
import os

# Generate random symmetric key
key_bytes = os.urandom(32)  # 256-bit key
jwk_oct = JWKOct(key=key_bytes)

# Symmetric keys are their own "public key"
same_key = jwk_oct.public_key()
assert jwk_oct is same_key

# Compute thumbprint
thumbprint_oct = jwk_oct.thumbprint()
print(f"Symmetric key thumbprint: {thumbprint_oct.hex()}")

# Use with HMAC algorithms
from josepy import HS256
message = b"Hello, HMAC!"
signature = HS256.sign(jwk_oct.key, message)
is_valid = HS256.verify(jwk_oct.key, message, signature)
print(f"HMAC signature valid: {is_valid}")

Key Loading and Generation

Loading Keys from Various Formats

from josepy import JWKRSA, JWKEC

# Load from PEM files
with open('private_key.pem', 'rb') as f:
    pem_data = f.read()
    jwk_rsa = JWKRSA.load(pem_data)

# Load password-protected keys
with open('encrypted_key.pem', 'rb') as f:
    encrypted_pem = f.read()
    jwk_rsa = JWKRSA.load(encrypted_pem, password=b'secret')

# Load from DER format
with open('key.der', 'rb') as f:
    der_data = f.read()
    jwk_rsa = JWKRSA.load(der_data)

# Load EC keys
with open('ec_key.pem', 'rb') as f:
    ec_pem = f.read()
    jwk_ec = JWKEC.load(ec_pem)

JSON Serialization and Deserialization

from josepy import JWKRSA, JWKEC, JWKOct

# Serialize any JWK to JSON
jwk_json = jwk.json_dumps(indent=2)

# Deserialize from JSON (automatic type detection)
from josepy import JWK
loaded_jwk = JWK.from_json(json.loads(jwk_json))

# Type-specific deserialization
rsa_jwk = JWKRSA.json_loads(jwk_json)
ec_jwk = JWKEC.json_loads(jwk_json)
oct_jwk = JWKOct.json_loads(jwk_json)

Key Thumbprints

JWK thumbprints provide unique identifiers for keys according to RFC 7638:

from josepy import JWKRSA
from cryptography.hazmat.primitives import hashes

# Default SHA-256 thumbprint
thumbprint = jwk.thumbprint()

# Custom hash function
thumbprint_sha1 = jwk.thumbprint(hashes.SHA1)
thumbprint_sha512 = jwk.thumbprint(hashes.SHA512)

# Thumbprints are deterministic and unique per key
assert jwk.thumbprint() == jwk.thumbprint()
assert public_jwk.thumbprint() == jwk.thumbprint()  # Same for public key

Error Handling

from josepy.errors import DeserializationError

try:
    # Invalid JSON structure
    jwk = JWKRSA.json_loads('{"invalid": "json"}')
except DeserializationError as e:
    print(f"Deserialization failed: {e}")

try:
    # Invalid key data
    jwk = JWKRSA.load(b"not a valid key")
except (ValueError, TypeError) as e:
    print(f"Key loading failed: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-josepy

docs

errors.md

index.md

interfaces.md

jwa.md

jwk.md

jws.md

utilities.md

tile.json