# 8. `example_ocsp.py` — OCSP response parsing and building
[← Example index](index.md) · [example_ocsp.py on Codeberg](https://codeberg.org/abbra/synta/src/branch/main/examples/example_ocsp.py)
Bindings: `OCSPResponse.from_der`, `OCSPResponse.from_pem`, `OCSPResponse.to_pem`,
and all `OCSPResponse` properties; `OCSPResponseBuilder` (`responder_name`,
`responder_key_hash`, `produced_at`, `add_response`, `build_tbs`, `assemble`);
`OCSPSingleResponse`; `NameBuilder`.
- Parse a successful OCSP response; print `status`, `response_type_oid`, length of `response_bytes`.
- Parse a non-successful response (e.g. `tryLater`); confirm `response_bytes` is `None`.
- Round-trip through `to_pem` / `from_pem`.
- Build an OCSP response from scratch with `OCSPResponseBuilder`: create a
`OCSPSingleResponse` entry (SHA-1 name hash of issuer name, fixed key hash,
status=good), set `responder_key_hash`, call `build_tbs()`, assemble with a
dummy signature; verify `status == "successful"` and response OID.
- Demonstrate the `responder_name` variant using a DER Name from `NameBuilder`.
## Source
```python
#!/usr/bin/env python3
"""
Example 7: OCSP response parsing and building.
Demonstrates: OCSPResponse.from_der, OCSPResponse.from_pem, OCSPResponse.to_pem,
and all OCSPResponse properties (status, response_type_oid, response_bytes, to_der);
OCSPResponseBuilder (responder_name, responder_key_hash, produced_at,
add_response, build_tbs, assemble); OCSPSingleResponse; NameBuilder.
"""
import hashlib
import synta
# Successful OCSP response (status=successful, contains BasicOCSPResponse)
_OCSP_SUCCESS_DER = bytes.fromhex(
"308201c40a0100a08201bd308201b906092b0601050507300101048201aa3082"
"01a630818fa216041451faf8a55a16074dc0703456a9a831e6f1f8d1b8180f32"
"303236303331303136303033345a30643062303a300906052b0e03021a050004"
"14bf7052c8b9c0f760c89123e099815eb2c0394226041451faf8a55a16074dc0"
"703456a9a831e6f1f8d1b802012a8000180f32303236303130313030303030305a"
"30110a0f32303236303130313030303030305a300d06092a864886f70d01010b"
"050003820101004de5a70defb08fe67d2ae1c4ab1ce22db43707db4ca3b65537"
"a99cf799acbda8194697f4aaf51ab13d7c36ff45abbff19ebd1071d329b30058"
"025185837125cba733ae42413ab56899d408b934121b16d8325331392a343772"
"083daa11da186476ea1fb4201c7bc1ac71d380357ff78071b18ecdbfdd6b1c0c"
"0ab996dae5475d849d27b2c22780b4dc76371eaaaa487d11ed13d48bfd121c9f"
"847f82ff70bf0143977d06f3f59e70cd8332976058e8dcf23bc1cd2b520ddc36"
"5a61cc81d6e2e881719056d4db925eb4d86ab4f116f6fc376365c85912dea5b3"
"4983cea9efe51cfbcbbabef08263aa39bc5f1742e9ecf0986b09025b56ed15a2"
"a2f8a012fbaad5"
)
# Non-successful OCSP response: tryLater (status=3), no responseBytes
_OCSP_TRYLATER_DER = bytes.fromhex("30030a0103")
def section(title):
print(f"\n{'─' * 60}\n{title}\n{'─' * 60}")
def demo_successful_response():
section("Successful OCSP response")
resp = synta.OCSPResponse.from_der(_OCSP_SUCCESS_DER)
print(f" status: {resp.status}")
print(f" response_type_oid: {resp.response_type_oid}")
print(f" response_bytes: <{len(resp.response_bytes)} bytes>")
print(f" to_der(): <{len(resp.to_der())} bytes>")
assert resp.status == "successful"
assert resp.response_type_oid is not None
# id-pkix-ocsp-basic = 1.3.6.1.5.5.7.48.1.1
assert str(resp.response_type_oid) == "1.3.6.1.5.5.7.48.1.1"
assert resp.response_bytes is not None
def demo_trylater_response():
section("Non-successful OCSP response — tryLater")
resp = synta.OCSPResponse.from_der(_OCSP_TRYLATER_DER)
print(f" status: {resp.status}")
print(f" response_type_oid: {resp.response_type_oid!r} (None for error responses)")
print(f" response_bytes: {resp.response_bytes!r} (None for error responses)")
assert resp.status == "tryLater"
assert resp.response_type_oid is None
assert resp.response_bytes is None
def demo_all_status_values():
section("All OCSP response status strings")
status_values = [
(0, "successful"),
(1, "malformedRequest"),
(2, "internalError"),
(3, "tryLater"),
(5, "sigRequired"),
(6, "unauthorized"),
]
for code, name in status_values:
# Build minimal OCSPResponse DER: SEQUENCE { ENUMERATED(code) }
der = bytes([0x30, 0x03, 0x0a, 0x01, code])
resp = synta.OCSPResponse.from_der(der)
print(f" ENUMERATED({code}) → status={resp.status!r}")
assert resp.status == name
def demo_to_der_roundtrip():
section("to_der() round-trip")
resp = synta.OCSPResponse.from_der(_OCSP_SUCCESS_DER)
der2 = resp.to_der()
assert der2 == _OCSP_SUCCESS_DER
print(" to_der() round-trip: OK")
def demo_pem_roundtrip():
section("from_pem / to_pem round-trip")
resp = synta.OCSPResponse.from_der(_OCSP_SUCCESS_DER)
pem = synta.OCSPResponse.to_pem(resp)
assert pem.startswith(b"-----BEGIN OCSP RESPONSE-----")
print(f" to_pem(): {len(pem)} bytes")
resp2 = synta.OCSPResponse.from_pem(pem)
assert resp2.status == resp.status
assert resp2.response_bytes == resp.response_bytes
print(" from_pem() round-trip: OK")
def demo_ocsp_builder():
section("OCSPResponseBuilder — build a complete OCSPResponse")
# id-sha1 AlgorithmIdentifier DER: SEQUENCE { OID 1.3.14.3.2.26 NULL }
SHA1_ALG = bytes.fromhex("300906052b0e03021a0500")
# sha256WithRSAEncryption AlgorithmIdentifier DER
SHA256_WITH_RSA = bytes.fromhex("300d06092a864886f70d01010b0500")
# Build an issuer name and compute SHA-1 of its DER for the name hash
name_der = synta.NameBuilder().common_name("Test CA").build()
name_hash = hashlib.sha1(name_der).digest()
# Use a fixed 20-byte key hash for demonstration
key_hash = bytes(20)
serial = bytes([0x01])
# Construct a single response entry (certificate status = good).
# OCSPSingleResponse is available via synta._synta (the native extension module).
single = synta.OCSPSingleResponse(
hash_algorithm_der=SHA1_ALG,
issuer_name_hash=name_hash,
issuer_key_hash=key_hash,
serial=serial,
status=0, # 0 = good
this_update="20260101000000Z",
next_update="20260701000000Z",
)
# Build the ResponseData TBS SEQUENCE
tbs = (
synta.OCSPResponseBuilder()
.responder_key_hash(key_hash)
.produced_at("20260101000000Z")
.add_response(single)
.build_tbs()
)
print(f" ResponseData DER: <{len(tbs)} bytes>")
# Assemble the full OCSPResponse with a dummy 256-byte signature
sig = bytes(256)
ocsp_der = synta.OCSPResponseBuilder.assemble(tbs, SHA256_WITH_RSA, sig)
print(f" OCSPResponse DER: <{len(ocsp_der)} bytes>")
# Parse and verify
resp = synta.OCSPResponse.from_der(ocsp_der)
assert resp.status == "successful", f"expected 'successful', got {resp.status!r}"
print(f" status: {resp.status}")
print(f" response_type_oid: {resp.response_type_oid}")
assert resp.response_type_oid is not None
# id-pkix-ocsp-basic = 1.3.6.1.5.5.7.48.1.1
assert str(resp.response_type_oid) == "1.3.6.1.5.5.7.48.1.1"
print(" OCSPResponseBuilder: OK")
# Also demonstrate responder_name variant (rebuild the single response object
# since the first add_response consumed it inside the builder)
single2 = synta.OCSPSingleResponse(
hash_algorithm_der=SHA1_ALG,
issuer_name_hash=name_hash,
issuer_key_hash=key_hash,
serial=serial,
status=0,
this_update="20260101000000Z",
next_update="20260701000000Z",
)
tbs_name = (
synta.OCSPResponseBuilder()
.responder_name(name_der)
.produced_at("20260101000000Z")
.add_response(single2)
.build_tbs()
)
ocsp_name_der = synta.OCSPResponseBuilder.assemble(tbs_name, SHA256_WITH_RSA, sig)
resp2 = synta.OCSPResponse.from_der(ocsp_name_der)
assert resp2.status == "successful"
print(f" responder_name variant: status={resp2.status!r} OK")
def main():
print("=" * 60)
print("Example 7: OCSP response parsing and building")
print("=" * 60)
demo_successful_response()
demo_trylater_response()
demo_all_status_values()
demo_to_der_roundtrip()
demo_pem_roundtrip()
demo_ocsp_builder()
print("\nAll OCSP examples completed.")
if __name__ == "__main__":
main()
```