# 7. `example_crl.py` — CRL parsing and building
[← Example index](index.md) · [example_crl.py on Codeberg](https://codeberg.org/abbra/synta/src/branch/main/examples/example_crl.py)
Bindings: `CertificateList.from_der`, `CertificateList.from_pem`,
`CertificateList.to_pem`, and all `CertificateList` properties
(`issuer`, `issuer_raw_der`, `this_update`, `next_update`, `signature_algorithm`,
`signature_algorithm_oid`, `signature_value`, `revoked_count`, `crl_number`);
`CertificateListBuilder` (`issuer`, `signature_algorithm`, `this_update`,
`next_update`, `revoke`, `build`, `assemble`); `NameBuilder`.
- Parse a CRL and print issuer, dates, algorithm, revoked count.
- Show `next_update` is `None` for CRLs that omit the field.
- Round-trip through `to_pem` / `from_pem`.
- Build a CRL from scratch with `CertificateListBuilder`: set issuer name via
`NameBuilder`, add two revoked entries with `revoke()` (one with keyCompromise
reason, one unspecified), call `build()` for the TBSCertList, assemble with a
dummy signature via `assemble()`; verify `revoked_count == 2`.
- Show `crl_number` returns `None` when the CRL Number extension is absent.
## Source
```python
#!/usr/bin/env python3
"""
Example 6: CRL (Certificate Revocation List) parsing and building.
Demonstrates: CertificateList.from_der, CertificateList.from_pem,
CertificateList.to_pem, and all CertificateList properties
(issuer, issuer_raw_der, this_update, next_update, signature_algorithm,
signature_algorithm_oid, signature_value, revoked_count, crl_number, to_der);
CertificateListBuilder (issuer, signature_algorithm, this_update, next_update,
revoke, build, assemble); NameBuilder.
"""
import synta
# Minimal CRL DER: RSA, CN=Test CA, thisUpdate=2026-01-01, nextUpdate=2026-07-01
_CRL_DER = bytes.fromhex(
"3082015a3044020101300d06092a864886f70d01010b050030123110300e0603"
"5504030c0754657374204341170d3236303130313030303030305a170d323630"
"3730313030303030305a300d06092a864886f70d01010b050003820101004195"
"618e7d6d6a6440919e872408156017646fd8cb0d617c6952755bfeb5b5f6391d"
"62b7257c7f94f461851f48ef9f9ade7c05e96679c25f8cb70b619bbc34b16a9"
"4ceaa072d66d2985407bcd6376925ba7d89fa6861cb7811c8571c9e6bb5d86a5"
"aad7cf3b62b63e7269b90d11d0e5a5cd54fa50edc6747e997f61738866b6f00"
"6c9bace2daba18b5bcedaa4bc25cf9add69b3680e6b170b3f57d82e74451d1a"
"2a1968db64c7d189c8869bd47a16bf91a0c6940cda54cdcbe355176792dc787"
"2f2a162a2482df9d7d1c6af568808962b23479bbd8229fac19cab7798d9a83f0"
"0ea4d83a6c61a4e720966e4ee4d9c3bbc3bdb94afde24fc70c8adb265ae15104"
)
def section(title):
print(f"\n{'─' * 60}\n{title}\n{'─' * 60}")
def demo_all_properties():
section("CertificateList — all properties")
crl = synta.CertificateList.from_der(_CRL_DER)
print(f" issuer: {crl.issuer}")
print(f" issuer_raw_der: <{len(crl.issuer_raw_der)} bytes>")
print(f" this_update: {crl.this_update}")
print(f" next_update: {crl.next_update!r}")
print(f" signature_algorithm: {crl.signature_algorithm}")
print(f" signature_algorithm_oid: {crl.signature_algorithm_oid}")
print(f" signature_value: <{len(crl.signature_value)} bytes>")
print(f" revoked_count: {crl.revoked_count}")
print(f" to_der: <{len(crl.to_der())} bytes>")
def demo_next_update_optional():
section("next_update — present vs absent")
crl = synta.CertificateList.from_der(_CRL_DER)
nu = crl.next_update
if nu is not None:
print(f" next_update present: {nu}")
else:
print(" next_update: None (CRL omits the field)")
# RFC 5280 §5.1.2.5: nextUpdate is OPTIONAL for indirect CRLs;
# the field is None when the CRL does not include it
def demo_to_der_roundtrip():
section("to_der() round-trip")
crl = synta.CertificateList.from_der(_CRL_DER)
der2 = crl.to_der()
assert der2 == _CRL_DER, "DER round-trip mismatch"
print(" to_der round-trip: OK (byte-identical)")
def demo_pem_roundtrip():
section("from_pem / to_pem round-trip")
crl = synta.CertificateList.from_der(_CRL_DER)
pem = synta.CertificateList.to_pem(crl)
assert pem.startswith(b"-----BEGIN X509 CRL-----")
print(f" to_pem(): {len(pem)} bytes")
crl2 = synta.CertificateList.from_pem(pem)
assert isinstance(crl2, synta.CertificateList)
assert crl2.issuer == crl.issuer
assert crl2.revoked_count == crl.revoked_count
print(f" from_pem(): issuer={crl2.issuer} revoked={crl2.revoked_count}")
print(" PEM round-trip: OK")
def demo_crl_builder():
section("CertificateListBuilder — build a CRL with two revoked entries")
# sha256WithRSAEncryption AlgorithmIdentifier DER
SHA256_WITH_RSA = bytes.fromhex("300d06092a864886f70d01010b0500")
# Build a Name for the issuer
name_der = synta.NameBuilder().common_name("Test CA").build()
# Build the TBSCertList SEQUENCE
tbs = (
synta.CertificateListBuilder()
.issuer(name_der)
.signature_algorithm(SHA256_WITH_RSA)
.this_update("20260101000000Z")
.next_update("20260701000000Z")
.revoke(bytes([0x01, 0x02, 0x03]), "20251201000000Z", 1) # keyCompromise
.revoke(bytes([0x04, 0x05, 0x06]), "20251201000000Z", None) # unspecified
.build()
)
print(f" TBSCertList DER: <{len(tbs)} bytes>")
# Assemble the full CertificateList with a dummy 256-byte signature
sig = bytes(256)
crl_der = synta.CertificateListBuilder.assemble(tbs, SHA256_WITH_RSA, sig)
print(f" CertificateList DER: <{len(crl_der)} bytes>")
# Parse and verify
crl = synta.CertificateList.from_der(crl_der)
assert crl.revoked_count == 2, f"expected 2, got {crl.revoked_count}"
print(f" revoked_count: {crl.revoked_count} (OK)")
print(f" issuer: {crl.issuer}")
print(f" this_update: {crl.this_update}")
print(f" next_update: {crl.next_update!r}")
# DER round-trip through to_der
assert crl.to_der() == crl_der
print(" to_der() round-trip: OK")
def demo_crl_number():
section("crl_number — present vs absent")
# The hard-coded _CRL_DER does not include a CRL Number extension,
# so crl_number returns None.
crl = synta.CertificateList.from_der(_CRL_DER)
n = crl.crl_number
if n is None:
print(" crl_number: None (CRL Number extension absent — expected)")
else:
print(f" crl_number: {n}")
assert n is None, f"expected None for test CRL, got {n!r}"
print(" crl_number absent: OK")
def main():
print("=" * 60)
print("Example 6: CRL parsing and building")
print("=" * 60)
demo_all_properties()
demo_next_update_optional()
demo_to_der_roundtrip()
demo_pem_roundtrip()
demo_crl_builder()
demo_crl_number()
print("\nAll CRL examples completed.")
if __name__ == "__main__":
main()
```