PEM encoding/decoding, low-level parsing utilities, cross-platform compatibility helpers, and version information. These utilities provide essential support functions for working with ASN.1 data, handling different encodings, and ensuring cross-platform compatibility.
Functions for converting between PEM (Privacy-Enhanced Mail) and DER (Distinguished Encoding Rules) formats.
def detect(byte_string):
"""
Detect if data contains PEM-encoded blocks.
Checks for the presence of BEGIN/END delimiters that indicate PEM format.
Args:
byte_string (bytes): Data to check for PEM encoding
Returns:
bool: True if PEM blocks are detected, False otherwise
"""
def armor(type_name, der_bytes, headers=None):
"""
Convert DER-encoded bytes to PEM format.
Wraps binary DER data in base64 encoding with appropriate delimiters.
Args:
type_name (str): PEM type name (e.g., "CERTIFICATE", "PRIVATE KEY")
der_bytes (bytes): DER-encoded binary data
headers (dict): Optional PEM headers as key-value pairs
Returns:
bytes: PEM-encoded data with delimiters and headers
"""
def unarmor(pem_bytes, multiple=False):
"""
Convert PEM-encoded bytes to DER format.
Extracts binary DER data from PEM format, removing delimiters and decoding base64.
Args:
pem_bytes (bytes): PEM-encoded data
multiple (bool): Whether to expect multiple PEM blocks
Returns:
tuple: (type_name, headers, der_bytes) for single block
list: List of (type_name, headers, der_bytes) tuples if multiple=True
"""Low-level functions for parsing and constructing ASN.1 data structures.
def parse(contents, strict=False):
"""
Parse ASN.1 BER/DER-encoded data.
Low-level parsing function that extracts ASN.1 tag, length, and content
without constructing high-level objects.
Args:
contents (bytes): BER/DER-encoded ASN.1 data
strict (bool): Whether to enforce strict DER parsing rules
Returns:
tuple: (class, method, tag, contents, trailer) where:
- class: Tag class (0=universal, 1=application, 2=context, 3=private)
- method: Construction method (0=primitive, 1=constructed)
- tag: Tag number
- contents: Content bytes
- trailer: Remaining bytes after this value
"""
def peek(contents):
"""
Look ahead to determine ASN.1 value information without parsing.
Examines the tag and length fields to determine the size of the next
ASN.1 value without fully parsing its contents.
Args:
contents (bytes): BER/DER-encoded ASN.1 data
Returns:
tuple: (class, method, tag, length) where:
- class: Tag class
- method: Construction method
- tag: Tag number
- length: Content length in bytes
"""
def emit(class_, method, tag, contents):
"""
Construct ASN.1 DER-encoded data from components.
Low-level function for building ASN.1 encoded data from tag and content.
Args:
class_ (int): Tag class (0=universal, 1=application, 2=context, 3=private)
method (int): Construction method (0=primitive, 1=constructed)
tag (int): Tag number
contents (bytes): Content data
Returns:
bytes: Complete DER-encoded ASN.1 data
"""Functions for converting between integers and byte representations.
def int_to_bytes(value, signed=False, width=None):
"""
Convert integer to bytes representation.
Converts a Python integer to a bytes object with proper encoding
for ASN.1 INTEGER types.
Args:
value (int): Integer value to convert
signed (bool): Whether to use signed representation
width (int): Optional fixed width in bytes
Returns:
bytes: Byte representation of the integer
"""
def int_from_bytes(value, signed=False):
"""
Convert bytes to integer representation.
Converts a bytes object to a Python integer, handling signed/unsigned
interpretations as needed for ASN.1 INTEGER types.
Args:
value (bytes): Byte data to convert
signed (bool): Whether to interpret as signed integer
Returns:
int: Integer value
"""Functions for handling IP addresses in certificates and other structures.
def inet_ntop(address_family, packed_ip):
"""
Convert packed IP address to string representation.
Similar to socket.inet_ntop but with cross-platform compatibility.
Args:
address_family (int): Address family (AF_INET or AF_INET6)
packed_ip (bytes): Packed IP address bytes
Returns:
str: String representation of IP address
"""
def inet_pton(address_family, ip_string):
"""
Convert string IP address to packed representation.
Similar to socket.inet_pton but with cross-platform compatibility.
Args:
address_family (int): Address family (AF_INET or AF_INET6)
ip_string (str): String IP address
Returns:
bytes: Packed IP address bytes
"""Functions for handling Uniform Resource Identifiers and Internationalized Resource Identifiers.
def uri_to_iri(uri_string):
"""
Convert URI to IRI (Internationalized Resource Identifier).
Converts percent-encoded URI to IRI with Unicode characters.
Args:
uri_string (str): URI string with percent encoding
Returns:
str: IRI string with Unicode characters
"""
def iri_to_uri(iri_string):
"""
Convert IRI to URI with percent encoding.
Converts IRI with Unicode characters to percent-encoded URI.
Args:
iri_string (str): IRI string with Unicode characters
Returns:
str: URI string with percent encoding
"""Cross-platform compatibility classes for timezone and date handling.
class timezone:
"""
Timezone class for Python 2 compatibility.
Provides timezone functionality similar to Python 3's datetime.timezone
for use in older Python versions.
"""
def __init__(self, offset, name=None):
"""
Initialize timezone with UTC offset.
Args:
offset (timedelta): UTC offset
name (str): Optional timezone name
"""
def utcoffset(self, dt):
"""Get UTC offset for this timezone."""
def tzname(self, dt):
"""Get timezone name."""
def dst(self, dt):
"""Get daylight saving time offset."""
def create_timezone(offset):
"""
Create timezone object with given UTC offset.
Factory function for creating timezone objects with cross-platform
compatibility.
Args:
offset (int): UTC offset in minutes
Returns:
timezone: Timezone object
"""
class utc_with_dst(timezone):
"""
UTC timezone class that properly handles DST queries.
Special UTC timezone implementation that returns None for DST
as required by the datetime module.
"""
class extended_date:
"""
Date class supporting year 0 and negative years.
Extends standard date functionality to handle years that
standard datetime cannot represent.
"""
def __init__(self, year, month, day):
"""
Initialize extended date.
Args:
year (int): Year (can be 0 or negative)
month (int): Month (1-12)
day (int): Day (1-31)
"""
@property
def year(self):
"""Get the year."""
@property
def month(self):
"""Get the month."""
@property
def day(self):
"""Get the day."""
class extended_datetime:
"""
Datetime class supporting year 0 and negative years.
Extends standard datetime functionality to handle years that
standard datetime cannot represent.
"""
def __init__(self, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
"""
Initialize extended datetime.
Args:
year (int): Year (can be 0 or negative)
month (int): Month (1-12)
day (int): Day (1-31)
hour (int): Hour (0-23)
minute (int): Minute (0-59)
second (int): Second (0-59)
microsecond (int): Microsecond (0-999999)
tzinfo: Timezone info
"""Classes providing Python 2/3 compatibility for data structures.
class OrderedDict:
"""
Ordered dictionary implementation for Python 2 compatibility.
Provides OrderedDict functionality on Python versions that don't
have it built-in. Maintains insertion order of dictionary items.
"""
def __init__(self, items=None):
"""
Initialize ordered dictionary.
Args:
items: Optional initial items (list of tuples or dict)
"""
def __getitem__(self, key):
"""Get item by key."""
def __setitem__(self, key, value):
"""Set item by key."""
def __delitem__(self, key):
"""Delete item by key."""
def __iter__(self):
"""Iterate over keys in insertion order."""
def keys(self):
"""Get dictionary keys in order."""
def values(self):
"""Get dictionary values in order."""
def items(self):
"""Get dictionary items in order."""Version constants and information about the asn1crypto library.
__version__ = "1.5.1"
"""
String representation of the library version.
Used for version checking and compatibility verification.
"""
__version_info__ = (1, 5, 1)
"""
Tuple representation of the library version.
Allows for programmatic version comparison:
- Major version: API compatibility level
- Minor version: Feature additions
- Patch version: Bug fixes and minor improvements
"""from asn1crypto import pem
# Check if data is PEM encoded
with open('certificate.pem', 'rb') as f:
data = f.read()
if pem.detect(data):
print("Data is PEM encoded")
# Extract DER data from PEM
type_name, headers, der_bytes = pem.unarmor(data)
print(f"PEM type: {type_name}")
print(f"DER size: {len(der_bytes)} bytes")
if headers:
print("PEM headers:")
for key, value in headers.items():
print(f" {key}: {value}")
# Convert back to PEM
new_pem = pem.armor(type_name, der_bytes, headers)
print("Successfully round-tripped PEM data")
else:
print("Data is already in DER format")
der_bytes = data
# Convert DER to PEM
pem_data = pem.armor('CERTIFICATE', der_bytes)
print("Converted DER to PEM format")from asn1crypto import pem
# Handle file with multiple certificates
with open('ca_chain.pem', 'rb') as f:
chain_data = f.read()
if pem.detect(chain_data):
# Extract all PEM blocks
pem_blocks = pem.unarmor(chain_data, multiple=True)
print(f"Found {len(pem_blocks)} PEM blocks:")
for i, (type_name, headers, der_bytes) in enumerate(pem_blocks):
print(f"Block {i+1}: {type_name} ({len(der_bytes)} bytes)")
# Parse each certificate
if type_name == 'CERTIFICATE':
from asn1crypto.x509 import Certificate
cert = Certificate.load(der_bytes)
print(f" Subject: {cert.subject.human_friendly}")from asn1crypto import parser
# Parse ASN.1 data at low level
der_data = b'\\x02\\x02\\x01\\x00' # INTEGER 256
class_, method, tag, contents, trailer = parser.parse(der_data)
print(f"Class: {class_}") # 0 (universal)
print(f"Method: {method}") # 0 (primitive)
print(f"Tag: {tag}") # 2 (INTEGER)
print(f"Contents: {contents.hex()}") # 0100
print(f"Trailer: {trailer}") # Empty
# Peek at data without full parsing
class_, method, tag, length = parser.peek(der_data)
print(f"Length: {length}") # 2
# Construct ASN.1 data
new_der = parser.emit(0, 0, 2, b'\\x01\\x00') # INTEGER 256
print(f"Constructed: {new_der.hex()}") # 02020100from asn1crypto import util
# Convert integers to bytes
big_number = 123456789012345678901234567890
byte_data = util.int_to_bytes(big_number)
print(f"Big number as bytes: {byte_data.hex()}")
# Convert back to integer
recovered = util.int_from_bytes(byte_data)
print(f"Recovered: {recovered}")
print(f"Match: {big_number == recovered}")
# Handle negative numbers
negative = -12345
signed_bytes = util.int_to_bytes(negative, signed=True)
print(f"Negative as bytes: {signed_bytes.hex()}")
recovered_negative = util.int_from_bytes(signed_bytes, signed=True)
print(f"Recovered negative: {recovered_negative}")
# Fixed width conversion
fixed_width = util.int_to_bytes(255, width=4)
print(f"Fixed width: {fixed_width.hex()}") # 000000fffrom asn1crypto import util
import socket
# Convert IP addresses
ipv4_str = "192.168.1.1"
ipv4_packed = util.inet_pton(socket.AF_INET, ipv4_str)
print(f"IPv4 packed: {ipv4_packed.hex()}")
ipv4_back = util.inet_ntop(socket.AF_INET, ipv4_packed)
print(f"IPv4 string: {ipv4_back}")
# Handle IPv6
ipv6_str = "2001:db8::1"
ipv6_packed = util.inet_pton(socket.AF_INET6, ipv6_str)
print(f"IPv6 packed: {ipv6_packed.hex()}")
ipv6_back = util.inet_ntop(socket.AF_INET6, ipv6_packed)
print(f"IPv6 string: {ipv6_back}")from asn1crypto import util
# Convert URI to IRI
uri = "http://example.com/path%20with%20spaces?param=%C3%BC"
iri = util.uri_to_iri(uri)
print(f"IRI: {iri}") # Unicode characters unescaped
# Convert back to URI
uri_back = util.iri_to_uri(iri)
print(f"URI: {uri_back}") # Percent-encodedfrom asn1crypto import util
from datetime import timedelta
# Create timezone
eastern = util.create_timezone(-300) # UTC-5 hours (300 minutes)
print(f"Timezone: {eastern}")
# Work with extended dates (supporting year 0)
old_date = util.extended_date(0, 1, 1) # Year 0 (1 BC)
print(f"Old date: {old_date.year}-{old_date.month:02d}-{old_date.day:02d}")
# Extended datetime
old_datetime = util.extended_datetime(0, 1, 1, 12, 0, 0)
print(f"Old datetime: {old_datetime}")
# Use OrderedDict for Python 2 compatibility
ordered = util.OrderedDict([('first', 1), ('second', 2), ('third', 3)])
print(f"Ordered keys: {list(ordered.keys())}")from asn1crypto import version
print(f"ASN1Crypto version: {version.__version__}")
print(f"Version info: {version.__version_info__}")
# Version comparison
if version.__version_info__ >= (1, 5, 0):
print("Using modern version with full features")
else:
print("Consider upgrading for latest features")
# Feature detection based on version
major, minor, patch = version.__version_info__
if major > 1 or (major == 1 and minor >= 4):
print("Extended time type support available")The utilities module provides several compatibility features: