OCSP Builder (ocspbuilder)
ocspbuilder is a Python library designed to simplify the creation and signing of Online Certificate Status Protocol (OCSP) requests and responses for X.509 certificates. It acts as a higher-level abstraction over the `cryptography` library, focusing on the builder pattern for OCSP structures. The current stable version is 0.10.2. While not updated frequently, it provides stable functionality for OCSP operations.
Warnings
- gotcha `ocspbuilder` primarily operates on `cryptography.x509.Certificate` and related objects for inputs (e.g., keys, certificates), not raw PEM/DER encoded bytes or strings. Users must first parse their certificate data into `cryptography` objects.
- gotcha The library simplifies OCSP message construction but does not manage the entire certificate chain validation or selection of the correct OCSP responder certificate. Users are responsible for providing the correct issuer certificate for requests and the correct OCSP responder certificate/key pair for signing responses.
- gotcha While `ocspbuilder` provides a stable API, its core functionality is deeply integrated with the `cryptography` library. Significant breaking changes or API shifts within `cryptography` (especially in `x509.ocsp` modules) could indirectly require updates to `ocspbuilder` or user code, even if `ocspbuilder` itself doesn't change.
- gotcha The PyPI metadata for `ocspbuilder` states `requires_python: None`, which can lead to ambiguity. GitHub CI/CD tests indicate compatibility with Python 3.7 through 3.10. Users on newer Python versions (e.g., 3.11+) should verify compatibility, as it largely depends on the `cryptography` dependency's support for those versions.
Install
-
pip install ocspbuilder
Imports
- OCSPRequestBuilder
from ocspbuilder import OCSPRequestBuilder
- OCSPResponseBuilder
from ocspbuilder import OCSPResponseBuilder
- Nonce
from ocspbuilder.extension import Nonce
Quickstart
import datetime
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from ocspbuilder import OCSPRequestBuilder, OCSPResponseBuilder
from ocspbuilder.extension import Nonce
# --- 1. Generate dummy certificates for a runnable example ---
# In a real scenario, you would load these from files/database.
# CA Key and Cert
ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
ca_subject = x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, u"OCSP Test CA")])
ca_cert = (
x509.CertificateBuilder()
.subject_name(ca_subject)
.issuer_name(ca_subject)
.public_key(ca_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.datetime.utcnow())
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
.add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
.sign(ca_key, hashes.SHA256(), default_backend())
)
# Issued Cert Key and Cert (signed by CA)
issued_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
issued_subject = x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, u"OCSP Test Cert")])
issued_cert = (
x509.CertificateBuilder()
.subject_name(issued_subject)
.issuer_name(ca_subject) # Signed by CA
.public_key(issued_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.datetime.utcnow())
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
.sign(ca_key, hashes.SHA256(), default_backend())
)
# --- 2. Build an OCSP Request ---
request_builder = OCSPRequestBuilder()
request_builder = request_builder.add_cert(issued_cert, ca_cert) # Cert to check, and its issuer
request_builder = request_builder.add_extension(Nonce.build()) # Add a common extension
ocsp_request = request_builder.build()
print("OCSP Request built successfully.")
print(f"Request bytes length: {len(ocsp_request.public_bytes(encoding=serialization.Encoding.DER))}")
# --- 3. Build an OCSP Response (e.g., 'good' status) ---
# The OCSP responder needs its own certificate and private key to sign the response.
# For this example, we'll reuse the CA's key/cert as the responder.
# In a production environment, this would be a dedicated responder cert.
ocsp_responder_key = ca_key
ocsp_responder_cert = ca_cert
response_builder = OCSPResponseBuilder(ocsp_request) # Initialize with the received request
response_builder = response_builder.add_response(
cert=issued_cert,
issuer=ca_cert,
cert_status=x509.ocsp.OCSPCertStatus.GOOD, # Example status
this_update=datetime.datetime.utcnow(),
next_update=datetime.datetime.utcnow() + datetime.timedelta(hours=1),
)
ocsp_response = response_builder.build(
signer_certificate=ocsp_responder_cert,
signer_private_key=ocsp_responder_key,
hash_algorithm=hashes.SHA256() # Algorithm used to sign the response
)
print("OCSP Response built successfully.")
print(f"Response bytes length: {len(ocsp_response.public_bytes(encoding=serialization.Encoding.DER))}")
# Optional: Verify the response using cryptography directly
# This demonstrates that ocspbuilder outputs standard cryptography objects
parsed_response = x509.ocsp.load_der_ocsp_response(ocsp_response.public_bytes(encoding=serialization.Encoding.DER))
print(f"Number of responses in parsed OCSP response: {len(parsed_response.responses)}")
print(f"Status for first cert in response: {parsed_response.responses[0].certificate_status}")