Scenario 6.2: OCSP Responder

Category: Revocation
Complexity: ⭐⭐⭐⭐ (High)
Prerequisites: CA infrastructure, OCSP signing certificate
Estimated Time: 30-45 Minutes


Description

This scenario describes the implementation of an OCSP Responder (Online Certificate Status Protocol, RFC 6960). OCSP enables real-time certificate status verification.

Advantages over CRL:


Workflow

flowchart LR CLIENT[Client] -->|OCSP Request| RESPONDER[OCSP Responder] RESPONDER --> DB[(Revocation DB)] RESPONDER -->|OCSP Response| CLIENT subgraph Response GOOD[good] REVOKED[revoked] UNKNOWN[unknown] end style RESPONDER fill:#e8f5e9


Code Example: Create OCSP Request

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Load certificate to check and issuer
var cert = ctx.LoadCertificate("server.crt.pem");
var issuer = ctx.LoadCertificate("intermediate-ca.crt.pem");
 
// Create OCSP request
var ocspRequest = ctx.CreateOcspRequest(
    certificate: cert,
    issuer: issuer,
    hashAlgorithm: HashAlgorithmName.SHA256,
    nonce: true  // Replay protection
);
 
// Request as DER
byte[] requestBytes = ocspRequest.Encode();
 
// Send request
using var http = new HttpClient();
var content = new ByteArrayContent(requestBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/ocsp-request");
 
var ocspUrl = ctx.GetOcspUrl(cert);  // Read AIA extension
var response = await http.PostAsync(ocspUrl, content);
 
var responseBytes = await response.Content.ReadAsByteArrayAsync();
 
// Parse response
var status = ctx.ParseOcspResponse(responseBytes, cert, issuer);
 
Console.WriteLine($"OCSP Status: {status.Status}");
Console.WriteLine($"  Produced: {status.ProducedAt}");
Console.WriteLine($"  This Update: {status.ThisUpdate}");
Console.WriteLine($"  Next Update: {status.NextUpdate}");

Code Example: Implement OCSP Responder

public class OcspResponder
{
    private readonly X509Certificate2 _responderCert;
    private readonly AsymmetricAlgorithm _responderKey;
    private readonly IRevocationDatabase _revocationDb;
    private readonly PqCryptoContext _ctx;
 
    public OcspResponder(
        X509Certificate2 responderCert,
        AsymmetricAlgorithm responderKey,
        IRevocationDatabase revocationDb)
    {
        _responderCert = responderCert;
        _responderKey = responderKey;
        _revocationDb = revocationDb;
        _ctx = PqCryptoContext.Initialize();
    }
 
    public byte[] ProcessRequest(byte[] requestBytes)
    {
        try
        {
            // Parse request
            var request = _ctx.ParseOcspRequest(requestBytes);
 
            // Response builder
            var responseBuilder = new OcspResponseBuilder();
 
            foreach (var certId in request.CertificateIds)
            {
                // Look up status in database
                var revocationInfo = _revocationDb.GetRevocationStatus(
                    certId.IssuerNameHash,
                    certId.IssuerKeyHash,
                    certId.SerialNumber
                );
 
                if (revocationInfo == null)
                {
                    // Unknown certificate
                    responseBuilder.AddStatus(certId, OcspCertStatus.Unknown);
                }
                else if (revocationInfo.IsRevoked)
                {
                    // Revoked
                    responseBuilder.AddStatus(
                        certId,
                        OcspCertStatus.Revoked,
                        revocationInfo.RevocationTime,
                        revocationInfo.RevocationReason
                    );
                }
                else
                {
                    // Valid
                    responseBuilder.AddStatus(certId, OcspCertStatus.Good);
                }
            }
 
            // Sign response
            return responseBuilder.Build(
                responderCert: _responderCert,
                responderKey: _responderKey,
                producedAt: DateTimeOffset.UtcNow,
                nextUpdate: DateTimeOffset.UtcNow.AddMinutes(5),
                nonce: request.Nonce,
                mode: CryptoMode.Hybrid
            );
        }
        catch (Exception ex)
        {
            // Internal Error Response
            return BuildErrorResponse(OcspResponseStatus.InternalError);
        }
    }
 
    private byte[] BuildErrorResponse(OcspResponseStatus status)
    {
        return _ctx.BuildOcspErrorResponse(status);
    }
}

Create OCSP Responder Certificate

// OCSP Responder needs special certificate
using var ocspKey = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65);
 
var dn = new DnBuilder()
    .AddCN("OCSP Responder - Example CA")
    .AddO("Example GmbH")
    .AddC("DE")
    .Build();
 
var csr = ctx.CreateCertificateRequest(ocspKey, dn);
 
var ocspCert = ctx.IssueCertificate(
    csr,
    issuerCert: caCert,
    issuerKey: caKey,
    validDays: 30,  // Short validity for OCSP responder
    extensions: new ExtBuilder()
        .BasicConstraints(ca: false, critical: true)
        // OCSP Signing Key Usage
        .KeyUsage(KeyUsageFlags.DigitalSignature, critical: true)
        // OCSP Signing EKU
        .ExtendedKeyUsage(ExtKeyUsage.OcspSigning)
        // id-pkix-ocsp-nocheck Extension (no revocation check for responder)
        .OcspNoCheck()
        .SubjectKeyIdentifier(ocspKey.PublicKey)
        .AuthorityKeyIdentifier(caCert)
        .Build()
);
 
ocspCert.ToPemFile("ocsp-responder.crt.pem");
ocspKey.ToEncryptedPemFile("ocsp-responder.key.pem", "OcspPassword!");

ASP.NET Core OCSP Endpoint

[ApiController]
[Route("ocsp")]
public class OcspController : ControllerBase
{
    private readonly OcspResponder _responder;
 
    public OcspController(OcspResponder responder)
    {
        _responder = responder;
    }
 
    // POST /ocsp
    [HttpPost]
    [Consumes("application/ocsp-request")]
    [Produces("application/ocsp-response")]
    public IActionResult Post()
    {
        using var ms = new MemoryStream();
        Request.Body.CopyTo(ms);
        var requestBytes = ms.ToArray();
 
        var responseBytes = _responder.ProcessRequest(requestBytes);
 
        return File(responseBytes, "application/ocsp-response");
    }
 
    // GET /ocsp/{base64-request} (for OCSP GET)
    [HttpGet("{encodedRequest}")]
    [Produces("application/ocsp-response")]
    public IActionResult Get(string encodedRequest)
    {
        var requestBytes = Convert.FromBase64String(
            Uri.UnescapeDataString(encodedRequest)
        );
 
        var responseBytes = _responder.ProcessRequest(requestBytes);
 
        return File(responseBytes, "application/ocsp-response");
    }
}

OCSP Response Status

Status HTTP Meaning
successful 200 Request processed
malformedRequest 200 Invalid request
internalError 200 Server error
tryLater 200 Temporarily unavailable
sigRequired 200 Signature required
unauthorized 200 Not authorized

Important: OCSP always returns HTTP 200! The status is encoded in the response.


Industry-Specific OCSP Requirements

Industry Response Caching Stapling High Availability
WebPKI Maximum 10 min Mandatory 99.9%
Enterprise Maximum 1 hour Recommended Per SLA
Financial Sector Maximum 5 min Mandatory 99.99%
Healthcare Maximum 1 hour Optional 99.9%

Relationship Scenario Description
Alternative 6.1 Create CRL Offline verification
Related 5.3 Revocation Check Client-side verification
Prerequisite 6.4 Revoke Certificate Revocation process

« ← 6.1 Create CRL | ↑ Revocation Overview | 6.3 Delta CRL → »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional