Scenario 6.2: OCSP Responder

Categoria: Revoca (Revocation)
Complessità: Alta
Prerequisiti: Infrastruttura CA, certificato OCSP Signing
Tempo stimato: 30-45 minuti


Descrizione

Questo scenario descrive l'implementazione di un OCSP Responder (Online Certificate Status Protocol, RFC 6960). OCSP consente la verifica in tempo reale dello stato del certificato.

Vantaggi rispetto a 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


Esempio codice: Creare OCSP Request

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}");

Esempio codice: Implementare 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
        {
            // 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);
    }
}

Creare certificato OCSP Responder

// 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

Stato HTTP Significato
successful 200 Request elaborata
malformedRequest 200 Request non valida
internalError 200 Errore server
tryLater 200 Temporaneamente non disponibile
sigRequired 200 Firma richiesta
unauthorized 200 Non autorizzato

Importante: OCSP restituisce sempre HTTP 200! Lo stato è codificato nella Response.


Requisiti OCSP specifici per settore

Settore Response Caching Stapling Alta disponibilità
WebPKI Max 10 min Obbligatorio 99.9%
Enterprise Max 1 ora Raccomandato Secondo SLA
Settore finanziario Max 5 min Obbligatorio 99.99%
Sanità Max 1 ora Opzionale 99.9%

Scenari correlati

Relazione Scenario Descrizione
Alternativa 6.1 Creare CRL Verifica offline
Correlato 5.3 Revocation Check Verifica lato client
Prerequisito 6.4 Revocare certificato Processo di revoca

« ← 6.1 Creare CRL | ↑ Panoramica revoca | 6.3 Delta-CRL → »


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