CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cryptography

Cryptographic recipes and primitives for Python developers

Pending
Overview
Eval results
Files

key-serialization.mddocs/

Key Serialization

Serialization and deserialization of cryptographic keys in various formats including PEM, DER, and SSH formats. Essential for key storage, transmission, and interoperability.

Core Imports

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import ssh, pkcs7, pkcs12

Capabilities

Key Loading Functions

def load_pem_private_key(data: bytes, password: bytes = None, backend=None) -> PrivateKey:
    """Load private key from PEM format"""

def load_der_private_key(data: bytes, password: bytes = None, backend=None) -> PrivateKey:
    """Load private key from DER format"""

def load_pem_public_key(data: bytes, backend=None) -> PublicKey:
    """Load public key from PEM format"""

def load_der_public_key(data: bytes, backend=None) -> PublicKey:
    """Load public key from DER format"""

def load_ssh_private_key(data: bytes, password: bytes = None, backend=None) -> PrivateKey:
    """Load SSH private key"""

def load_ssh_public_key(data: bytes, backend=None) -> PublicKey:
    """Load SSH public key"""

Encoding Formats

class Encoding:
    PEM: Encoding          # Base64 with headers
    DER: Encoding          # Binary ASN.1
    OpenSSH: Encoding      # SSH format
    Raw: Encoding          # Raw bytes
    X962: Encoding         # X9.62 format for EC keys

class PrivateFormat:
    PKCS8: PrivateFormat                # PKCS#8 (preferred)
    TraditionalOpenSSL: PrivateFormat   # Traditional format
    Raw: PrivateFormat                  # Raw key bytes
    OpenSSH: PrivateFormat              # SSH private key format

class PublicFormat:
    SubjectPublicKeyInfo: PublicFormat  # X.509 SubjectPublicKeyInfo (preferred)
    PKCS1: PublicFormat                 # PKCS#1 RSA format
    OpenSSH: PublicFormat               # SSH public key format
    Raw: PublicFormat                   # Raw key bytes
    CompressedPoint: PublicFormat       # Compressed EC point
    UncompressedPoint: PublicFormat     # Uncompressed EC point

Encryption Algorithms

class KeySerializationEncryption:
    """Abstract base for key encryption"""

class NoEncryption(KeySerializationEncryption):
    """No encryption for private keys"""

class BestAvailableEncryption(KeySerializationEncryption):
    def __init__(self, password: bytes):
        """
        Best available encryption for private keys.
        
        Args:
            password (bytes): Password for key encryption
        """

SSH Key Support

def ssh_key_fingerprint(public_key, algorithm=hashes.SHA256()) -> bytes:
    """Generate SSH key fingerprint"""

class SSHCertificate:
    """SSH certificate representation"""
    
    @property
    def public_key(self):
        """Certificate public key"""
    
    @property
    def serial(self) -> int:
        """Certificate serial number"""

Usage Examples

Save and Load RSA Keys

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# Generate RSA key
private_key = rsa.generate_private_key(65537, 2048)
public_key = private_key.public_key()

# Save private key (encrypted)
encrypted_private = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.BestAvailableEncryption(b'mypassword')
)

# Save private key (unencrypted - less secure)
unencrypted_private = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

# Save public key  
public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# Write to files
with open('private_key_encrypted.pem', 'wb') as f:
    f.write(encrypted_private)

with open('public_key.pem', 'wb') as f:
    f.write(public_pem)

# Load keys back
with open('private_key_encrypted.pem', 'rb') as f:
    loaded_private = serialization.load_pem_private_key(
        f.read(),
        password=b'mypassword'
    )

with open('public_key.pem', 'rb') as f:
    loaded_public = serialization.load_pem_public_key(f.read())

print("Keys loaded successfully")

SSH Key Format

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization, hashes

# Generate key
private_key = rsa.generate_private_key(65537, 2048)
public_key = private_key.public_key()

# SSH public key format
ssh_public = public_key.public_bytes(
    encoding=serialization.Encoding.OpenSSH,
    format=serialization.PublicFormat.OpenSSH
)

# SSH private key format
ssh_private = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.OpenSSH,
    encryption_algorithm=serialization.NoEncryption()
)

print("SSH public key:")
print(ssh_public.decode())

# Generate SSH key fingerprint
fingerprint = serialization.ssh_key_fingerprint(public_key, hashes.SHA256())
print(f"SSH fingerprint: {fingerprint.hex()}")

# Save SSH keys
with open('id_rsa', 'wb') as f:
    f.write(ssh_private)

with open('id_rsa.pub', 'wb') as f:
    f.write(ssh_public)

Ed25519 Key Serialization

from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization

# Generate Ed25519 key
private_key = ed25519.Ed25519PrivateKey.generate()
public_key = private_key.public_key()

# Raw format (32 bytes each)
private_raw = private_key.private_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PrivateFormat.Raw,
    encryption_algorithm=serialization.NoEncryption()
)

public_raw = public_key.public_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PublicFormat.Raw
)

print(f"Ed25519 private key (raw): {private_raw.hex()} ({len(private_raw)} bytes)")
print(f"Ed25519 public key (raw): {public_raw.hex()} ({len(public_raw)} bytes)")

# PEM format
private_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

print("Ed25519 PEM private key:")
print(private_pem.decode())

Key Format Conversion

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa

# Load existing key (any format)
with open('existing_key.pem', 'rb') as f:
    private_key = serialization.load_pem_private_key(f.read(), password=None)

# Convert to different formats
formats = {
    'pkcs8_pem': (serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8),
    'pkcs8_der': (serialization.Encoding.DER, serialization.PrivateFormat.PKCS8),
    'traditional_pem': (serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL),
    'ssh': (serialization.Encoding.PEM, serialization.PrivateFormat.OpenSSH),
}

for name, (encoding, format_type) in formats.items():
    try:
        key_bytes = private_key.private_bytes(
            encoding=encoding,
            format=format_type,
            encryption_algorithm=serialization.NoEncryption()
        )
        
        with open(f'key_{name}', 'wb') as f:
            f.write(key_bytes)
            
        print(f"Converted to {name}")
    except Exception as e:
        print(f"Failed to convert to {name}: {e}")

Secure Key Storage Class

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
import os
import getpass

class SecureKeyStorage:
    def save_private_key(self, private_key, filepath: str, password: str = None):
        """Save private key with optional password protection"""
        if password is None:
            password = getpass.getpass("Enter password for private key: ")
        
        if password:
            encryption = serialization.BestAvailableEncryption(password.encode())
        else:
            encryption = serialization.NoEncryption()
        
        key_bytes = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=encryption
        )
        
        with open(filepath, 'wb') as f:
            f.write(key_bytes)
        
        print(f"Private key saved to {filepath}")
    
    def load_private_key(self, filepath: str, password: str = None):
        """Load private key with password prompt if needed"""
        with open(filepath, 'rb') as f:
            key_data = f.read()
        
        # Try without password first
        try:
            return serialization.load_pem_private_key(key_data, password=None)
        except TypeError:
            # Key is encrypted, need password
            if password is None:
                password = getpass.getpass("Enter password for private key: ")
            
            return serialization.load_pem_private_key(key_data, password=password.encode())
    
    def save_public_key(self, public_key, filepath: str, format_type: str = 'pem'):
        """Save public key in specified format"""
        if format_type.lower() == 'ssh':
            key_bytes = public_key.public_bytes(
                encoding=serialization.Encoding.OpenSSH,
                format=serialization.PublicFormat.OpenSSH
            )
        else:  # PEM
            key_bytes = public_key.public_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PublicFormat.SubjectPublicKeyInfo
            )
        
        with open(filepath, 'wb') as f:
            f.write(key_bytes)
        
        print(f"Public key saved to {filepath}")

# Usage
from cryptography.hazmat.primitives.asymmetric import rsa

key_storage = SecureKeyStorage()

# Generate and save key pair
private_key = rsa.generate_private_key(65537, 2048)
public_key = private_key.public_key()

key_storage.save_private_key(private_key, 'my_private_key.pem', 'secure_password')
key_storage.save_public_key(public_key, 'my_public_key.pem')
key_storage.save_public_key(public_key, 'my_public_key.ssh', format_type='ssh')

# Load key back
loaded_key = key_storage.load_private_key('my_private_key.pem', 'secure_password')
print("Key loaded successfully")

Security Considerations

  • Password Protection: Always encrypt private keys when storing
  • Secure Deletion: Securely wipe unencrypted key data from memory
  • Format Selection: Use PKCS#8 for interoperability
  • Key Validation: Validate loaded keys before use
  • Access Control: Restrict file permissions on key files (600/400)
  • Key Rotation: Regularly rotate and re-encrypt stored keys

PKCS#7/CMS Support

PKCS#7 (Cryptographic Message Syntax) for packaging and signing certificates and data.

def load_pem_pkcs7_certificates(data: bytes) -> List[Certificate]:
    """
    Load certificates from PKCS#7 PEM data.
    
    Args:
        data (bytes): PEM-encoded PKCS#7 data
        
    Returns:
        List[Certificate]: List of certificates from PKCS#7 structure
    """

def load_der_pkcs7_certificates(data: bytes) -> List[Certificate]:
    """
    Load certificates from PKCS#7 DER data.
    
    Args:
        data (bytes): DER-encoded PKCS#7 data
        
    Returns:
        List[Certificate]: List of certificates from PKCS#7 structure
    """

def serialize_certificates(certificates: List[Certificate], encoding: Encoding) -> bytes:
    """
    Serialize certificates to PKCS#7 format.
    
    Args:
        certificates: List of certificates to serialize
        encoding: Encoding format (PEM or DER)
        
    Returns:
        bytes: PKCS#7 encoded certificate data
    """

class PKCS7Options:
    Text: PKCS7Options                 # Add text/plain MIME type
    Binary: PKCS7Options               # Don't translate to canonical MIME
    DetachedSignature: PKCS7Options    # Don't embed data in PKCS#7

PKCS#12 Support

PKCS#12 format for packaging private keys with certificates, commonly used for key/certificate bundles.

def load_key_and_certificates(data: bytes, password: bytes = None) -> Tuple[PrivateKey, Certificate, List[Certificate]]:
    """
    Load private key and certificates from PKCS#12 data.
    
    Args:
        data (bytes): PKCS#12 data
        password (bytes): Password for PKCS#12 (None if unencrypted)
        
    Returns:
        Tuple containing:
        - PrivateKey: The private key (or None)
        - Certificate: The certificate (or None) 
        - List[Certificate]: Additional certificates
    """

def load_pkcs12(data: bytes, password: bytes = None) -> PKCS12KeyAndCertificates:
    """
    Load PKCS#12 data into structured object.
    
    Args:
        data (bytes): PKCS#12 data
        password (bytes): Password for PKCS#12
        
    Returns:
        PKCS12KeyAndCertificates: Structured PKCS#12 data
    """

def serialize_key_and_certificates(name: bytes, key: PrivateKey, cert: Certificate, cas: List[Certificate] = None, encryption_algorithm=None) -> bytes:
    """
    Serialize key and certificates to PKCS#12 format.
    
    Args:
        name (bytes): Friendly name for the key/cert
        key: Private key to include
        cert: Certificate to include
        cas: Additional certificates to include
        encryption_algorithm: Encryption for the PKCS#12 data
        
    Returns:
        bytes: PKCS#12 encoded data
    """

class PKCS12KeyAndCertificates:
    """Container for PKCS#12 key and certificate data"""
    
    @property
    def key(self) -> PrivateKey:
        """Private key from PKCS#12"""
    
    @property
    def cert(self) -> PKCS12Certificate:
        """Main certificate from PKCS#12"""
    
    @property 
    def additional_certificates(self) -> List[PKCS12Certificate]:
        """Additional certificates from PKCS#12"""

class PKCS12Certificate:
    """Certificate from PKCS#12 with additional metadata"""
    
    @property
    def certificate(self) -> Certificate:
        """The X.509 certificate"""
    
    @property
    def friendly_name(self) -> bytes:
        """Friendly name for the certificate"""

PKCS#7 Usage Example

from cryptography.hazmat.primitives.serialization import pkcs7
from cryptography import x509
from cryptography.hazmat.primitives import serialization

# Load certificates from PKCS#7
with open('certificates.p7b', 'rb') as f:
    p7b_data = f.read()

certificates = pkcs7.load_pem_pkcs7_certificates(p7b_data)
print(f"Loaded {len(certificates)} certificates from PKCS#7")

# Serialize certificates back to PKCS#7
pkcs7_data = pkcs7.serialize_certificates(
    certificates,
    encoding=serialization.Encoding.PEM
)

with open('output.p7b', 'wb') as f:
    f.write(pkcs7_data)

PKCS#12 Usage Example

from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
import datetime

# Create a key and self-signed certificate
private_key = rsa.generate_private_key(65537, 2048)
public_key = private_key.public_key()

# Build certificate
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
    x509.NameAttribute(x509.NameOID.COMMON_NAME, "Test Certificate")
]))
builder = builder.issuer_name(x509.Name([
    x509.NameAttribute(x509.NameOID.COMMON_NAME, "Test Certificate")
]))
builder = builder.public_key(public_key)
builder = builder.serial_number(1)
builder = builder.not_valid_before(datetime.datetime.utcnow())
builder = builder.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))

certificate = builder.sign(private_key, hashes.SHA256())

# Create PKCS#12 bundle
p12_data = pkcs12.serialize_key_and_certificates(
    name=b"My Certificate",
    key=private_key,
    cert=certificate,
    cas=None,  # No additional certificates
    encryption_algorithm=serialization.BestAvailableEncryption(b"password123")
)

# Save PKCS#12 file
with open('bundle.p12', 'wb') as f:
    f.write(p12_data)

# Load PKCS#12 bundle back
with open('bundle.p12', 'rb') as f:
    p12_data = f.read()

loaded_key, loaded_cert, additional_certs = pkcs12.load_key_and_certificates(
    p12_data, 
    password=b"password123"
)

print(f"Loaded private key: {loaded_key}")
print(f"Loaded certificate: {loaded_cert.subject}")
print(f"Additional certificates: {len(additional_certs)}")

Install with Tessl CLI

npx tessl i tessl/pypi-cryptography

docs

aead-ciphers.md

asymmetric-cryptography.md

hash-functions.md

index.md

key-derivation.md

key-serialization.md

message-authentication.md

symmetric-ciphers.md

symmetric-encryption.md

two-factor-auth.md

utilities.md

x509-certificates.md

tile.json