Cryptographic recipes and primitives for Python developers
—
Serialization and deserialization of cryptographic keys in various formats including PEM, DER, and SSH formats. Essential for key storage, transmission, and interoperability.
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import ssh, pkcs7, pkcs12def 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"""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 pointclass 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
"""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"""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")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)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())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}")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")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#7PKCS#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"""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)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