Szenario 6.2: OCSP Responder

Kategorie: Widerruf (Revocation)
Komplexität: ⭐⭐⭐⭐ (Hoch)
Voraussetzungen: CA-Infrastruktur, OCSP-Signing-Zertifikat
Geschätzte Zeit: 30-45 Minuten


Beschreibung

Dieses Szenario beschreibt die Implementierung eines OCSP Responders (Online Certificate Status Protocol, RFC 6960). OCSP ermöglicht Echtzeit-Prüfung des Zertifikatsstatus.

Vorteile gegenüber 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-Beispiel: OCSP Request erstellen

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Zu prüfendes Zertifikat und Issuer laden
var cert = ctx.LoadCertificate("server.crt.pem");
var issuer = ctx.LoadCertificate("intermediate-ca.crt.pem");
 
// OCSP Request erstellen
var ocspRequest = ctx.CreateOcspRequest(
    certificate: cert,
    issuer: issuer,
    hashAlgorithm: HashAlgorithmName.SHA256,
    nonce: true  // Replay-Schutz
);
 
// Request als DER
byte[] requestBytes = ocspRequest.Encode();
 
// Request senden
using var http = new HttpClient();
var content = new ByteArrayContent(requestBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/ocsp-request");
 
var ocspUrl = ctx.GetOcspUrl(cert);  // AIA Extension auslesen
var response = await http.PostAsync(ocspUrl, content);
 
var responseBytes = await response.Content.ReadAsByteArrayAsync();
 
// Response parsen
var status = ctx.ParseOcspResponse(responseBytes, cert, issuer);
 
Console.WriteLine($"OCSP Status: {status.Status}");
Console.WriteLine($"  Produziert: {status.ProducedAt}");
Console.WriteLine($"  This Update: {status.ThisUpdate}");
Console.WriteLine($"  Next Update: {status.NextUpdate}");

Code-Beispiel: OCSP Responder implementieren

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
        {
            // Request parsen
            var request = _ctx.ParseOcspRequest(requestBytes);
 
            // Response Builder
            var responseBuilder = new OcspResponseBuilder();
 
            foreach (var certId in request.CertificateIds)
            {
                // Status in Datenbank nachschlagen
                var revocationInfo = _revocationDb.GetRevocationStatus(
                    certId.IssuerNameHash,
                    certId.IssuerKeyHash,
                    certId.SerialNumber
                );
 
                if (revocationInfo == null)
                {
                    // Unbekanntes Zertifikat
                    responseBuilder.AddStatus(certId, OcspCertStatus.Unknown);
                }
                else if (revocationInfo.IsRevoked)
                {
                    // Widerrufen
                    responseBuilder.AddStatus(
                        certId,
                        OcspCertStatus.Revoked,
                        revocationInfo.RevocationTime,
                        revocationInfo.RevocationReason
                    );
                }
                else
                {
                    // Gültig
                    responseBuilder.AddStatus(certId, OcspCertStatus.Good);
                }
            }
 
            // Response signieren
            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);
    }
}

OCSP Responder Zertifikat erstellen

// OCSP Responder braucht spezielles Zertifikat
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,  // Kurze Gültigkeit für 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 (keine Revocation-Prüfung für 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} (für 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 Bedeutung
successful 200 Request verarbeitet
malformedRequest 200 Ungültiger Request
internalError 200 Server-Fehler
tryLater 200 Temporär nicht verfügbar
sigRequired 200 Signatur erforderlich
unauthorized 200 Nicht autorisiert

Wichtig: OCSP gibt immer HTTP 200 zurück! Der Status ist in der Response kodiert.


Branchenspezifische OCSP-Anforderungen

Branche Response Caching Stapling Hochverfügbarkeit
WebPKI Max 10 Min Pflicht 99.9%
Enterprise Max 1 Stunde Empfohlen Je nach SLA
Finanzsektor Max 5 Min Pflicht 99.99%
Healthcare Max 1 Stunde Optional 99.9%

Verwandte Szenarien

Beziehung Szenario Beschreibung
Alternative 6.1 CRL erstellen Offline-Prüfung
Verwandt 5.3 Revocation Check Client-seitige Prüfung
Voraussetzung 6.4 Zertifikat widerrufen Widerrufsprozess

« ← 6.1 CRL erstellen | ↑ Widerruf-Übersicht | 6.3 Delta-CRL → »


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