PyCryptodome is a self-contained Python package of low-level cryptographic primitives
—
Utilities for encoding, decoding, and serializing cryptographic objects in standard formats. Provides support for PEM (Privacy-Enhanced Mail) and PKCS#8 formats commonly used for key storage and exchange.
Privacy-Enhanced Mail (PEM) format provides ASCII-armored encoding of binary cryptographic data with clear text headers and optional encryption.
def encode(data, marker, passphrase=None, randfunc=None):
"""
Encode binary data in PEM format.
Parameters:
- data (bytes): Binary data to encode
- marker (str): PEM marker string (e.g., 'RSA PRIVATE KEY', 'CERTIFICATE')
- passphrase (bytes/str): Optional password for encryption
- randfunc (callable): Random function for encryption (default: get_random_bytes)
Returns:
bytes: PEM-encoded data with -----BEGIN/END----- headers
"""
def decode(pem_data, passphrase=None):
"""
Decode PEM-formatted data.
Parameters:
- pem_data (bytes/str): PEM-encoded data with headers
- passphrase (bytes/str): Password for encrypted PEM data
Returns:
tuple: (decoded_data, marker, encrypted)
- decoded_data (bytes): Binary data
- marker (str): PEM marker string
- encrypted (bool): Whether data was encrypted
Raises:
ValueError: Invalid PEM format or incorrect passphrase
"""PKCS#8 (Private-Key Information Syntax Standard) provides a format for storing private key information with optional encryption and algorithm identification.
def wrap(private_key, key_oid, passphrase=None, protection=None, prot_params=None, key_params=None, randfunc=None):
"""
Wrap private key in PKCS#8 format.
Parameters:
- private_key (bytes): Private key data to wrap
- key_oid (str): Object identifier for key algorithm
- passphrase (bytes/str): Optional password for encryption
- protection (str): Encryption algorithm ('PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC',
'PBKDF2WithHMAC-SHA1AndAES128-CBC', 'PBKDF2WithHMAC-SHA1AndAES256-CBC',
'scryptAndAES128-CBC', 'scryptAndAES256-CBC')
- prot_params (dict): Protection algorithm parameters
- key_params (bytes): Optional algorithm parameters
- randfunc (callable): Random function for encryption
Returns:
bytes: PKCS#8 wrapped private key (DER-encoded)
"""
def unwrap(p8_private_key, passphrase=None):
"""
Unwrap PKCS#8 private key.
Parameters:
- p8_private_key (bytes): PKCS#8 wrapped private key (DER or PEM)
- passphrase (bytes/str): Password for encrypted keys
Returns:
tuple: (key_data, key_oid, key_params)
- key_data (bytes): Private key data
- key_oid (str): Key algorithm OID
- key_params (bytes): Optional algorithm parameters
Raises:
ValueError: Invalid PKCS#8 format or incorrect passphrase
"""from Crypto.IO import PEM
from Crypto.PublicKey import RSA
# Generate RSA key for examples
key = RSA.generate(2048)
private_der = key.export_key('DER')
# Encode private key in PEM format (unencrypted)
pem_data = PEM.encode(private_der, "RSA PRIVATE KEY")
print(pem_data.decode())
# Output:
# -----BEGIN RSA PRIVATE KEY-----
# MIIEpAIBAAKCAQEA...
# -----END RSA PRIVATE KEY-----
# Encode with password protection
encrypted_pem = PEM.encode(private_der, "RSA PRIVATE KEY", passphrase="secret123")
# Decode PEM data
decoded_data, marker, was_encrypted = PEM.decode(pem_data)
print(f"Marker: {marker}, Encrypted: {was_encrypted}")
# Decode encrypted PEM
decrypted_data, marker, was_encrypted = PEM.decode(encrypted_pem, passphrase="secret123")from Crypto.IO import PEM
# Custom data with custom marker
custom_data = b"Custom binary data for application"
pem_encoded = PEM.encode(custom_data, "CUSTOM DATA")
# Decode custom PEM
decoded, marker, encrypted = PEM.decode(pem_encoded)
assert decoded == custom_data
assert marker == "CUSTOM DATA"from Crypto.IO import PKCS8
from Crypto.PublicKey import RSA, ECC
# RSA private key in PKCS#8 format
rsa_key = RSA.generate(2048)
rsa_der = rsa_key.export_key('DER')
# Wrap RSA key in PKCS#8 (unencrypted)
pkcs8_data = PKCS8.wrap(rsa_der, "1.2.840.113549.1.1.1") # RSA OID
# Wrap with AES-256 encryption
encrypted_pkcs8 = PKCS8.wrap(
rsa_der,
"1.2.840.113549.1.1.1",
passphrase="strong_password",
protection="PBKDF2WithHMAC-SHA1AndAES256-CBC"
)
# Unwrap PKCS#8 data
key_data, key_oid, key_params = PKCS8.unwrap(pkcs8_data)
print(f"Key algorithm OID: {key_oid}")
# Unwrap encrypted PKCS#8
decrypted_key, oid, params = PKCS8.unwrap(encrypted_pkcs8, passphrase="strong_password")from Crypto.IO import PKCS8
from Crypto.PublicKey import ECC
# Generate ECC key
ecc_key = ECC.generate(curve='P-256')
ecc_der = ecc_key.export_key(format='DER', use_pkcs8=False)
# Wrap ECC key in PKCS#8 with scrypt protection
pkcs8_ecc = PKCS8.wrap(
ecc_der,
"1.2.840.10045.2.1", # EC public key OID
passphrase="ecc_password",
protection="scryptAndAES256-CBC"
)
# Unwrap ECC key
ecc_data, oid, params = PKCS8.unwrap(pkcs8_ecc, passphrase="ecc_password")from Crypto.PublicKey import RSA
from Crypto.IO import PEM, PKCS8
# Generate and export key in different formats
key = RSA.generate(2048)
# Method 1: Direct PEM export (built-in)
pem_direct = key.export_key('PEM', passphrase="pass123")
# Method 2: Manual PEM encoding
der_data = key.export_key('DER')
pem_manual = PEM.encode(der_data, "RSA PRIVATE KEY", passphrase="pass123")
# Method 3: PKCS#8 format
pkcs8_wrapped = PKCS8.wrap(
der_data,
key.oid, # RSA OID from key object
passphrase="pass123",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC"
)
# Convert PKCS#8 to PEM
pkcs8_pem = PEM.encode(pkcs8_wrapped, "PRIVATE KEY", passphrase="pass123")PEM encoding supports the following encryption algorithms for password protection:
PKCS#8 supports various protection schemes combining key derivation with encryption:
# PBKDF2-based schemes
"PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC" # Legacy
"PBKDF2WithHMAC-SHA1AndAES128-CBC" # Standard
"PBKDF2WithHMAC-SHA1AndAES192-CBC" # High security
"PBKDF2WithHMAC-SHA1AndAES256-CBC" # Highest security
# scrypt-based schemes (more secure against hardware attacks)
"scryptAndAES128-CBC" # Modern standard
"scryptAndAES256-CBC" # Modern high securityfrom Crypto.IO import PEM
def is_pem_format(data):
"""Check if data is in PEM format."""
if isinstance(data, bytes):
data = data.decode('ascii', errors='ignore')
return data.strip().startswith('-----BEGIN') and '-----END' in data
# Example usage
pem_data = b"-----BEGIN RSA PRIVATE KEY-----\n..."
if is_pem_format(pem_data):
decoded, marker, encrypted = PEM.decode(pem_data)from Crypto.IO import PKCS8
def detect_private_key_format(der_data):
"""Detect if DER data is PKCS#8 or traditional format."""
try:
# Try PKCS#8 unwrap
key_data, oid, params = PKCS8.unwrap(der_data)
return "PKCS#8"
except ValueError:
# Probably traditional format (PKCS#1 for RSA, SEC1 for ECC, etc.)
return "Traditional"
# Example usage
key = RSA.generate(2048)
traditional_der = key.export_key('DER')
pkcs8_der = PKCS8.wrap(traditional_der, key.oid)
print(detect_private_key_format(traditional_der)) # "Traditional"
print(detect_private_key_format(pkcs8_der)) # "PKCS#8"# RSA
RSA_OID = "1.2.840.113549.1.1.1"
# Elliptic Curve
EC_PUBLIC_KEY_OID = "1.2.840.10045.2.1"
# DSA
DSA_OID = "1.2.840.10040.4.1"
# Ed25519
ED25519_OID = "1.3.101.112"
# Ed448
ED448_OID = "1.3.101.113"ValueError: Invalid format, incorrect passphrase, or malformed dataTypeError: Incorrect parameter typesUnicodeDecodeError: Invalid PEM text encodingKeyError: Missing required ASN.1 fields in PKCS#8 structuresInstall with Tessl CLI
npx tessl i tessl/pypi-pycryptodome