OCSP Responder Framework

0.5.0 · active · verified Sun Apr 12

ocspresponder is an RFC 6960 compliant OCSP Responder framework written in Python 3.5+. It provides a foundation for building an OCSP responder service, leveraging the ocspbuilder and asn1crypto libraries for cryptographic operations, and using Bottle for the HTTP server. It is currently in an alpha development stage (version 0.5.0) and is not recommended for production use.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to instantiate the `OCSPResponder` class. It includes dummy certificate generation and simple functions for validating certificate status and retrieving issuer certificates. In a real application, `issuer_cert`, `ocsp_cert`, and `ocsp_key` would be loaded securely, and `retrieve_certificate_status` and `retrieve_issuer_certificate` would interact with a robust certificate database. The example also shows how it would be integrated into a Bottle web server.

import os
from datetime import datetime, timedelta
from typing import Optional
from ocspresponder import OCSPResponder, CertificateStatus
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from cryptography import x509
from ocspbuilder import OCSPResponseBuilder

# NOTE: This quickstart uses dummy certificates and keys for demonstration.
# In a real scenario, these would be loaded from secure storage.
# Also, ocspresponder is currently alpha; not for production use.

# Generate dummy CA and OCSP responder certificates/keys for demonstration
def generate_dummy_certs():
    ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    ca_subject = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"California"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My CA"),
        x509.NameAttribute(NameOID.COMMON_NAME, u"My Root 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.utcnow())
        .not_valid_after(datetime.utcnow() + timedelta(days=365))
        .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
        .sign(ca_key, hashes.SHA256())
    )

    ocsp_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    ocsp_subject = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"California"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My OCSP Responder"),
        x509.NameAttribute(NameOID.COMMON_NAME, u"ocsp.example.com"),
    ])
    ocsp_cert = (
        x509.CertificateBuilder()
        .subject_name(ocsp_subject)
        .issuer_name(ca_subject)
        .public_key(ocsp_key.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(datetime.utcnow())
        .not_valid_after(datetime.utcnow() + timedelta(days=90))
        .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
        .add_extension(x509.ExtendedKeyUsage([x509.ExtendedKeyUsageOID.OCSP_SIGNING]), critical=True)
        .sign(ca_key, hashes.SHA256())
    )
    
    return ca_cert, ca_key, ocsp_cert, ocsp_key

CA_CERT, CA_KEY, OCSP_CERT, OCSP_KEY = generate_dummy_certs()

# Example data store for certificate statuses
# In a real application, this would be a database or other persistent store.
dummy_cert_db = {
    12345: (CertificateStatus.good, None, CA_CERT.public_bytes(serialization.Encoding.PEM)),
    67890: (CertificateStatus.revoked, datetime.utcnow() - timedelta(days=5), CA_CERT.public_bytes(serialization.Encoding.PEM))
}

# Custom function to validate a certificate serial number
def validate_cert_status(serial: int) -> (CertificateStatus, Optional[datetime]):
    status, revoked_at, _ = dummy_cert_db.get(serial, (CertificateStatus.unknown, None, None))
    return status, revoked_at

# Custom function to retrieve the issuer certificate for a given serial
def get_issuer_certificate(serial: int) -> Optional[bytes]:
    # In a real scenario, you'd find the actual issuer of the certificate
    # identified by 'serial'. For this dummy example, we return the CA_CERT.
    _, _, issuer_cert_pem = dummy_cert_db.get(serial, (None, None, None))
    return issuer_cert_pem

# Instantiate the OCSP Responder
responder = OCSPResponder(
    issuer_cert=CA_CERT.public_bytes(serialization.Encoding.PEM),
    ocsp_cert=OCSP_CERT.public_bytes(serialization.Encoding.PEM),
    ocsp_key=OCSP_KEY.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    ),
    retrieve_certificate_status=validate_cert_status,
    retrieve_issuer_certificate=get_issuer_certificate
)

print("OCSPResponder instantiated successfully.")
print("This is a framework. To run a server, you would integrate this into an HTTP server (e.g., Bottle).")
print("For example, with Bottle:")
print("from bottle import run, request, post")
print("@post('/ocsp')")
print("def ocsp_service():")
print("    return responder.handle_ocsp_request(request.body.read())")
print("run(host='localhost', port=8080)")

view raw JSON →