Cross-platform cryptographic library providing TLS sockets, asymmetric/symmetric encryption, and key operations using OS-native crypto libraries
—
Parsing and handling of cryptographic keys and certificates from various formats including DER, PEM, PKCS#8, and PKCS#12. These functions provide format detection and conversion capabilities for interoperability with different cryptographic systems.
Parse X.509 certificates from DER or PEM format data.
def parse_certificate(data: bytes) -> Certificate:
"""
Parse an X.509 certificate from DER or PEM data.
Parameters:
- data: bytes - Certificate data in DER or PEM format
Returns:
Certificate object with parsed certificate information
Raises:
ValueError if data is not a valid certificate
"""Parse private keys from various formats including PKCS#8, PKCS#1, and OpenSSL formats.
def parse_private(data: bytes) -> PrivateKey:
"""
Parse a private key from DER or PEM encoded data.
Parameters:
- data: bytes - Private key data in supported formats:
- PKCS#8 (encrypted or unencrypted)
- PKCS#1 RSA private key
- OpenSSL traditional format
- SEC1 EC private key
Returns:
PrivateKey object for the parsed key
Raises:
ValueError if data is not a valid private key
AsymmetricKeyError if key format is unsupported
"""Parse public keys from DER or PEM format data.
def parse_public(data: bytes) -> PublicKey:
"""
Parse a public key from DER or PEM encoded data.
Parameters:
- data: bytes - Public key data in supported formats:
- X.509 SubjectPublicKeyInfo
- PKCS#1 RSA public key
- RFC 3279 formats
Returns:
PublicKey object for the parsed key
Raises:
ValueError if data is not a valid public key
AsymmetricKeyError if key format is unsupported
"""Parse PKCS#12 bundles containing certificates, private keys, and certificate chains.
def parse_pkcs12(data: bytes, password: bytes = None) -> Tuple[Certificate, PrivateKey, List[Certificate]]:
"""
Parse a PKCS#12 bundle containing certificate and private key.
Parameters:
- data: bytes - PKCS#12 bundle data
- password: bytes - Password for encrypted bundle (None for unencrypted)
Returns:
Tuple of (end_entity_certificate, private_key, intermediate_certificates)
Raises:
ValueError if data is not a valid PKCS#12 bundle
AsymmetricKeyError if password is incorrect or bundle is corrupted
"""from oscrypto.keys import parse_certificate
import os
def load_certificate_file(file_path: str):
"""Load and parse a certificate file."""
with open(file_path, 'rb') as f:
cert_data = f.read()
try:
certificate = parse_certificate(cert_data)
print(f"Certificate loaded from: {file_path}")
print(f"Subject: {certificate.subject}")
print(f"Issuer: {certificate.issuer}")
print(f"Serial Number: {certificate.serial_number}")
print(f"Valid From: {certificate.not_valid_before}")
print(f"Valid Until: {certificate.not_valid_after}")
print(f"Key Algorithm: {certificate.public_key.algorithm}")
if hasattr(certificate.public_key, 'bit_size'):
print(f"Key Size: {certificate.public_key.bit_size} bits")
return certificate
except ValueError as e:
print(f"Error parsing certificate: {e}")
return None
# Example usage
cert_files = [
'server.crt',
'ca-certificate.pem',
'client.der'
]
for cert_file in cert_files:
if os.path.exists(cert_file):
load_certificate_file(cert_file)
print("-" * 50)from oscrypto.keys import parse_private
from oscrypto.errors import AsymmetricKeyError
import getpass
def load_private_key_file(file_path: str, password: str = None):
"""Load and parse a private key file with optional password."""
with open(file_path, 'rb') as f:
key_data = f.read()
# Convert password to bytes if provided
password_bytes = password.encode('utf-8') if password else None
try:
# First try without password
private_key = parse_private(key_data)
print(f"Private key loaded from: {file_path} (unencrypted)")
except (ValueError, AsymmetricKeyError) as e:
if "password" in str(e).lower() or "encrypted" in str(e).lower():
# Key is encrypted, prompt for password if not provided
if not password_bytes:
password = getpass.getpass("Enter private key password: ")
password_bytes = password.encode('utf-8')
try:
from oscrypto.asymmetric import load_private_key
private_key = load_private_key(key_data, password_bytes)
print(f"Private key loaded from: {file_path} (encrypted)")
except Exception as e2:
print(f"Error loading encrypted private key: {e2}")
return None
else:
print(f"Error parsing private key: {e}")
return None
# Display key information
print(f"Algorithm: {private_key.algorithm}")
if hasattr(private_key, 'bit_size'):
print(f"Key Size: {private_key.bit_size} bits")
if hasattr(private_key, 'curve'):
print(f"Curve: {private_key.curve}")
return private_key
# Example usage
key_files = [
'private_key.pem',
'encrypted_key.p8',
'rsa_key.der'
]
for key_file in key_files:
if os.path.exists(key_file):
load_private_key_file(key_file)
print("-" * 50)from oscrypto.keys import parse_pkcs12
from oscrypto.errors import AsymmetricKeyError
import getpass
def process_pkcs12_bundle(file_path: str, password: str = None):
"""Process a PKCS#12 bundle file."""
with open(file_path, 'rb') as f:
p12_data = f.read()
# Prompt for password if not provided
if not password:
password = getpass.getpass(f"Enter password for {file_path}: ")
password_bytes = password.encode('utf-8') if password else None
try:
certificate, private_key, intermediates = parse_pkcs12(p12_data, password_bytes)
print(f"PKCS#12 bundle processed: {file_path}")
print(f"End-entity certificate: {certificate.subject}")
print(f"Private key algorithm: {private_key.algorithm}")
print(f"Intermediate certificates: {len(intermediates)}")
# Display certificate chain
print("\nCertificate Chain:")
print(f"1. {certificate.subject} (end-entity)")
for i, intermediate in enumerate(intermediates, 2):
print(f"{i}. {intermediate.subject} (intermediate)")
# Verify private key matches certificate
cert_public_key = certificate.public_key
private_public_key = private_key.public_key
# Simple check - compare key algorithms and sizes
keys_match = (cert_public_key.algorithm == private_public_key.algorithm)
if hasattr(cert_public_key, 'bit_size') and hasattr(private_public_key, 'bit_size'):
keys_match = keys_match and (cert_public_key.bit_size == private_public_key.bit_size)
print(f"\nPrivate key matches certificate: {keys_match}")
return certificate, private_key, intermediates
except AsymmetricKeyError as e:
print(f"Error processing PKCS#12 bundle: {e}")
return None, None, []
# Example usage
p12_files = [
'client.p12',
'server.pfx',
'identity.p12'
]
for p12_file in p12_files:
if os.path.exists(p12_file):
process_pkcs12_bundle(p12_file)
print("=" * 60)from oscrypto.keys import parse_certificate, parse_private, parse_public, parse_pkcs12
from oscrypto.errors import AsymmetricKeyError
def identify_crypto_file(file_path: str):
"""Identify the type and contents of a cryptographic file."""
with open(file_path, 'rb') as f:
data = f.read()
print(f"Analyzing file: {file_path}")
print(f"File size: {len(data)} bytes")
# Check if it's text (PEM) or binary (DER/P12)
is_text = all(byte < 128 for byte in data[:100])
print(f"Format: {'PEM/Text' if is_text else 'Binary (DER/P12)'}")
if is_text:
# Look for PEM markers
data_str = data.decode('utf-8', errors='ignore')
pem_types = []
if '-----BEGIN CERTIFICATE-----' in data_str:
pem_types.append('Certificate')
if '-----BEGIN PRIVATE KEY-----' in data_str:
pem_types.append('PKCS#8 Private Key')
if '-----BEGIN RSA PRIVATE KEY-----' in data_str:
pem_types.append('PKCS#1 RSA Private Key')
if '-----BEGIN EC PRIVATE KEY-----' in data_str:
pem_types.append('SEC1 EC Private Key')
if '-----BEGIN PUBLIC KEY-----' in data_str:
pem_types.append('Public Key')
if pem_types:
print(f"PEM content types: {', '.join(pem_types)}")
# Try parsing as different types
results = {}
# Try certificate
try:
cert = parse_certificate(data)
results['certificate'] = f"X.509 Certificate - Subject: {cert.subject}"
except:
pass
# Try private key
try:
key = parse_private(data)
results['private_key'] = f"Private Key - Algorithm: {key.algorithm}"
if hasattr(key, 'bit_size'):
results['private_key'] += f", {key.bit_size} bits"
except:
pass
# Try public key
try:
pub_key = parse_public(data)
results['public_key'] = f"Public Key - Algorithm: {pub_key.algorithm}"
if hasattr(pub_key, 'bit_size'):
results['public_key'] += f", {pub_key.bit_size} bits"
except:
pass
# Try PKCS#12 (typically requires password, so this might fail)
try:
cert, key, intermediates = parse_pkcs12(data, None)
results['pkcs12'] = f"PKCS#12 Bundle - Cert: {cert.subject}, Key: {key.algorithm}, Intermediates: {len(intermediates)}"
except:
# Try with empty password
try:
cert, key, intermediates = parse_pkcs12(data, b'')
results['pkcs12'] = f"PKCS#12 Bundle (empty password) - Cert: {cert.subject}, Key: {key.algorithm}, Intermediates: {len(intermediates)}"
except:
pass
if results:
print("Successful parses:")
for parse_type, description in results.items():
print(f" {parse_type}: {description}")
else:
print("Could not parse as any recognized cryptographic format")
print()
# Example usage - analyze all crypto files in directory
import glob
crypto_extensions = ['*.pem', '*.crt', '*.cer', '*.der', '*.key', '*.p8', '*.p12', '*.pfx']
crypto_files = []
for extension in crypto_extensions:
crypto_files.extend(glob.glob(extension))
for crypto_file in crypto_files:
identify_crypto_file(crypto_file)from oscrypto.keys import parse_private, parse_certificate
from oscrypto.asymmetric import dump_private_key, dump_certificate, dump_public_key
import base64
def convert_key_format(input_file: str, output_file: str, output_format: str = 'pem'):
"""Convert cryptographic files between formats."""
with open(input_file, 'rb') as f:
input_data = f.read()
try:
# Try parsing as private key
private_key = parse_private(input_data)
# Export in requested format
if output_format.lower() == 'der':
output_data = dump_private_key(private_key)
else: # PEM
der_data = dump_private_key(private_key)
pem_data = base64.b64encode(der_data).decode('ascii')
# Add PEM wrapper
output_data = (
"-----BEGIN PRIVATE KEY-----\n" +
'\n'.join(pem_data[i:i+64] for i in range(0, len(pem_data), 64)) +
"\n-----END PRIVATE KEY-----\n"
).encode('ascii')
with open(output_file, 'wb') as f:
f.write(output_data)
print(f"Converted private key from {input_file} to {output_file} ({output_format.upper()})")
return
except:
pass
try:
# Try parsing as certificate
certificate = parse_certificate(input_data)
# Export in requested format
if output_format.lower() == 'der':
output_data = dump_certificate(certificate)
else: # PEM
der_data = dump_certificate(certificate)
pem_data = base64.b64encode(der_data).decode('ascii')
# Add PEM wrapper
output_data = (
"-----BEGIN CERTIFICATE-----\n" +
'\n'.join(pem_data[i:i+64] for i in range(0, len(pem_data), 64)) +
"\n-----END CERTIFICATE-----\n"
).encode('ascii')
with open(output_file, 'wb') as f:
f.write(output_data)
print(f"Converted certificate from {input_file} to {output_file} ({output_format.upper()})")
return
except Exception as e:
print(f"Error converting {input_file}: {e}")
# Example usage
conversions = [
('server.crt', 'server.der', 'der'),
('client.der', 'client.pem', 'pem'),
('private.p8', 'private.pem', 'pem')
]
for input_file, output_file, format_type in conversions:
if os.path.exists(input_file):
convert_key_format(input_file, output_file, format_type)Install with Tessl CLI
npx tessl i tessl/pypi-oscrypto