# Certificate
`Certificate` represents an RFC 5280 X.509 v3 certificate. Parsing is lazy: `from_der`
performs only a 4-operation shallow scan (outer SEQUENCE TLV + TBSCertificate SEQUENCE TLV);
the full RFC 5280 decode is deferred to the first field access, after which every property
is cached in an `OnceLock` and returned in O(1) on subsequent calls.
## Construction
```python
Certificate.from_der(data: bytes) -> Certificate
```
Parse a DER-encoded X.509 certificate. Performs a shallow 4-operation structural scan on
construction; the full decode is deferred to the first field access.
```python
Certificate.full_from_der(data: bytes) -> Certificate
```
Parse and perform a complete RFC 5280 decode immediately. All field getters are on the warm
path from the first call. Use when benchmarking full parse cost or when latency on the first
field access must be avoided.
```python
Certificate.from_pem(data: bytes) -> Certificate | list[Certificate]
```
Parse a PEM-encoded certificate. Returns a single `Certificate` for a single PEM block, or
a `list[Certificate]` when multiple blocks are present.
```python
Certificate.to_pem(cert_or_list) -> bytes
```
Encode one or more `Certificate` objects back to PEM.
```python
Certificate.from_pyca(pyca_cert: object) -> Certificate
```
Convert a `cryptography.x509.Certificate` to a `synta.Certificate` by serialising to DER
and calling `from_der`. Raises `ImportError` if the `cryptography` package is not installed.
```python
cert.to_pyca() -> cryptography.x509.Certificate
```
Return a `cryptography.x509.Certificate` backed by the same DER bytes (no re-encoding).
Use for cryptographic operations not directly supported by synta. Raises `ImportError` if
the `cryptography` package is not installed.
## Properties
All properties are cached after first access. `to_der` skips the lock entirely (direct
reference to the stored bytes, no copy).
### Identity
| `serial_number` | `int` | Arbitrary-precision Python int (RFC 5280: up to 20 bytes) |
| `version` | `int \| None` | Version field: `None` = v1, `1` = v2, `2` = v3 |
| `issuer` | `str` | RFC 4514 DN string |
| `issuer_raw_der` | `bytes` | Raw DER of issuer Name SEQUENCE |
| `subject` | `str` | RFC 4514 DN string |
| `subject_raw_der` | `bytes` | Raw DER of subject Name SEQUENCE |
### Validity
| `not_before` | `str` | `"YYMMDDHHMMSSZ"` (UTCTime) or `"YYYYMMDDHHMMSSZ"` (GeneralizedTime) |
| `not_after` | `str` | Same format as `not_before` |
| `not_before_utc` | `datetime.datetime` | notBefore as a UTC-aware `datetime.datetime` |
| `not_after_utc` | `datetime.datetime` | notAfter as a UTC-aware `datetime.datetime` |
### Signature
| `signature_algorithm` | `str` | Human-readable algorithm name (e.g. `"RSA"`, `"ECDSA"`, `"ML-DSA-65"`) or dotted OID for unrecognised algorithms |
| `signature_algorithm_oid` | `ObjectIdentifier` | Always machine-readable dotted OID |
| `signature_algorithm_params` | `bytes \| None` | DER-encoded parameters, or `None` when absent (e.g. Ed25519) |
| `signature_hash_algorithm_name` | `str \| None` | Lowercase hash name (e.g. `"sha256"`, `"sha1"`) implied by the signature algorithm OID, or `None` for Ed25519, ML-DSA, etc. |
| `signature_value` | `bytes` | Raw signature bytes (BIT STRING value, unused-bit prefix stripped) |
### Subject Public Key
| `public_key_algorithm` | `str` | Human-readable name or dotted OID |
| `public_key_algorithm_oid` | `ObjectIdentifier` | Dotted OID (e.g. `"1.2.840.10045.2.1"` for id-ecPublicKey) |
| `public_key_algorithm_params` | `bytes \| None` | DER parameters (curve OID for EC; `None` for RSA/EdDSA) |
| `public_key` | `bytes` | Raw SubjectPublicKeyInfo BIT STRING content (unused-bit byte stripped) |
| `subject_public_key_info_der` | `bytes` | Full SubjectPublicKeyInfo SEQUENCE DER (for SKI/AKI computation) |
### Raw DER Spans
| `to_der()` | `bytes` | Complete original certificate DER (zero-copy) |
| `tbs_bytes` | `bytes` | TBSCertificate DER — the bytes that were signed |
| `tbs_certificate_der` | `bytes` | Alias for `tbs_bytes` |
| `issuer_raw_der` | `bytes` | Issuer Name SEQUENCE DER |
| `subject_raw_der` | `bytes` | Subject Name SEQUENCE DER |
| `extensions_der` | `bytes \| None` | SEQUENCE OF Extension DER, or `None` for v1/v2 |
## Methods
| `to_der()` | `()` | `bytes` | Original DER bytes (zero-copy) |
| `get_extension_value_der` | `(oid: str \| ObjectIdentifier)` | `bytes \| None` | Return the extnValue content bytes for the named extension OID, or `None` if absent. Scans in O(n) over the already-parsed Extension list. Raises `ValueError` for invalid OID strings. |
| `subject_alt_names()` | `()` | `list[AnyGeneralName]` | SAN entries as typed `synta.general_name` objects (`DNSName`, `IPAddress`, `RFC822Name`, etc.). Returns `[]` when no SAN extension is present. |
| `general_names` | `(oid: str \| ObjectIdentifier)` | `list[AnyGeneralName]` | Decode any extension whose value is `SEQUENCE OF GeneralName`. Returns typed `synta.general_name` objects. |
| `certificate_policies()` | `()` | `list[PolicyInformation]` | Decode the CertificatePolicies extension (OID 2.5.29.32). Returns `[]` when absent. |
| `verify_issued_by` | `(issuer: Certificate)` | `None` | Verify this certificate was signed by `issuer`. Raises `ValueError` on failure. |
| `fingerprint` | `(algorithm: str)` | `bytes` | Certificate fingerprint using the named hash (e.g. `"sha256"`). Raises `ValueError` for unknown algorithms. |
## Full class stub
```python
class Certificate:
@staticmethod
def from_der(data: bytes) -> Certificate: ...
@staticmethod
def full_from_der(data: bytes) -> Certificate: ...
@staticmethod
def from_pyca(pyca_cert: object) -> Certificate: ...
def to_pyca(self) -> object: ...
def __repr__(self) -> str: ...
# Identity
serial_number: int
version: int | None
issuer: str
issuer_raw_der: bytes
subject: str
subject_raw_der: bytes
# Validity
not_before: str
not_after: str
not_before_utc: datetime.datetime
not_after_utc: datetime.datetime
# Signature
signature_algorithm: str
signature_algorithm_oid: ObjectIdentifier
signature_algorithm_params: bytes | None
signature_value: bytes
# Subject public key
public_key_algorithm: str
public_key_algorithm_oid: ObjectIdentifier
public_key_algorithm_params: bytes | None
public_key: bytes
subject_public_key_info_der: bytes
# Raw DER spans
def to_der(self) -> bytes: ...
tbs_bytes: bytes
tbs_certificate_der: bytes # alias for tbs_bytes
extensions_der: bytes | None
# Methods
def get_extension_value_der(self, oid: str) -> bytes | None: ...
def subject_alt_names(self) -> list: ... # list of typed synta.general_name objects
def general_names(self, oid: str | ObjectIdentifier) -> list: ...
def certificate_policies(self) -> list[PolicyInformation]: ...
def verify_issued_by(self, issuer: Certificate) -> None: ...
def fingerprint(self, algorithm: str) -> bytes: ...
```
## Usage
```python
import synta
# Parse a DER-encoded certificate
with open('cert.der', 'rb') as f:
cert = synta.Certificate.from_der(f.read())
# Identity fields
print(cert.subject) # RFC 4514-style DN string
print(cert.issuer) # RFC 4514-style DN string
print(cert.serial_number) # Python int (arbitrary precision)
print(cert.version) # None (v1), 1 (v2), or 2 (v3)
# Validity
print(cert.not_before) # String, e.g. "230101000000Z"
print(cert.not_after) # String, e.g. "320101000000Z"
# Signature algorithm
print(cert.signature_algorithm) # Human-readable name or dotted OID
print(cert.signature_algorithm_oid) # Dotted OID string (always machine-readable)
print(cert.signature_algorithm_params) # DER bytes or None (e.g. None for Ed25519)
print(cert.signature_value) # Raw signature bytes
# Subject public key
print(cert.public_key_algorithm) # Human-readable name or dotted OID
print(cert.public_key_algorithm_oid) # Dotted OID string
print(cert.public_key_algorithm_params) # DER bytes or None (EC: curve OID)
print(cert.public_key) # Raw SubjectPublicKeyInfo bit-string bytes
# Raw DER spans
print(cert.to_der()) # Complete original certificate DER bytes
print(cert.tbs_bytes) # TBSCertificate DER (the bytes that were signed)
print(cert.issuer_raw_der) # Issuer Name SEQUENCE DER
print(cert.subject_raw_der) # Subject Name SEQUENCE DER
print(cert.extensions_der) # Extensions SEQUENCE OF DER, or None for v1/v2
# Look up a single extension by OID — returns the extnValue OCTET STRING
# content (the DER-encoded extension-specific structure), or None if absent.
san_der = cert.get_extension_value_der("2.5.29.17") # SubjectAltName
bc_der = cert.get_extension_value_der("2.5.29.19") # BasicConstraints
eku_der = cert.get_extension_value_der("2.5.29.37") # ExtendedKeyUsage
```
### SAN access
```python
import ipaddress
import synta.general_name as gn
# High-level: subject_alt_names() returns typed synta.general_name objects
for name in cert.subject_alt_names():
if isinstance(name, gn.DNSName):
print("dns:", name.value)
elif isinstance(name, gn.IPAddress):
print("ip:", ipaddress.ip_address(name.address))
elif isinstance(name, gn.RFC822Name):
print("email:", name.value)
elif isinstance(name, gn.UniformResourceIdentifier):
print("uri:", name.value)
elif isinstance(name, gn.DirectoryName):
attrs = synta.parse_name_attrs(name.name_der) # [(oid_str, value_str), …]
print("dirname:", attrs)
```
### Iterating extensions manually
```python
# Iterate all extensions when you need every extension
ext_der = cert.extensions_der
if ext_der:
dec = synta.Decoder(ext_der, synta.Encoding.DER)
exts = dec.decode_sequence()
while not exts.is_empty():
ext_tlv = exts.decode_raw_tlv() # bytes covering one Extension TLV
```
### Signature verification
```python
# Verify a certificate was signed by a CA certificate
ca_cert = synta.Certificate.from_der(open("ca.der", "rb").read())
end_cert = synta.Certificate.from_der(open("end.der", "rb").read())
try:
end_cert.verify_issued_by(ca_cert)
print("Signature valid")
except ValueError as e:
print(f"Invalid: {e}")
```
See also:
- [CSR](csr.md) for PKCS#10 certificate signing requests
- [GeneralName](general-name.md) for `synta.general_name` tag constants
- [PyCA Interoperability](pyca-interop.md) for converting to/from `cryptography`
- [X.509 Path Validation](x509-verification.md) for chain verification