Scenarij 6.2: OCSP Responder

Kategorija: Opoziv (Revocation)
Složenost: ⭐⭐⭐⭐ (Visoka)
Preduvjeti: CA infrastruktura, OCSP-Signing certifikat
Procijenjeno vrijeme: 30-45 minuta


Opis

Ovaj scenarij opisuje implementaciju OCSP Respondera (Online Certificate Status Protocol, RFC 6960). OCSP omogućuje provjeru statusa certifikata u stvarnom vremenu.

Prednosti nad CRL-om:


Tijek rada

flowchart LR CLIENT[Klijent] -->|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


Primjer koda: Kreiranje OCSP zahtjeva

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Učitavanje certifikata za provjeru i Issuera
var cert = ctx.LoadCertificate("server.crt.pem");
var issuer = ctx.LoadCertificate("intermediate-ca.crt.pem");
 
// Kreiranje OCSP zahtjeva
var ocspRequest = ctx.CreateOcspRequest(
    certificate: cert,
    issuer: issuer,
    hashAlgorithm: HashAlgorithmName.SHA256,
    nonce: true  // Zaštita od ponavljanja
);
 
// Request kao DER
byte[] requestBytes = ocspRequest.Encode();
 
// Slanje zahtjeva
using var http = new HttpClient();
var content = new ByteArrayContent(requestBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/ocsp-request");
 
var ocspUrl = ctx.GetOcspUrl(cert);  // Čitanje AIA ekstenzije
var response = await http.PostAsync(ocspUrl, content);
 
var responseBytes = await response.Content.ReadAsByteArrayAsync();
 
// Parsiranje odgovora
var status = ctx.ParseOcspResponse(responseBytes, cert, issuer);
 
Console.WriteLine($"OCSP status: {status.Status}");
Console.WriteLine($"  Proizvedeno: {status.ProducedAt}");
Console.WriteLine($"  This Update: {status.ThisUpdate}");
Console.WriteLine($"  Next Update: {status.NextUpdate}");

Primjer koda: Implementacija OCSP Respondera

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
        {
            // Parsiranje zahtjeva
            var request = _ctx.ParseOcspRequest(requestBytes);
 
            // Response Builder
            var responseBuilder = new OcspResponseBuilder();
 
            foreach (var certId in request.CertificateIds)
            {
                // Traženje statusa u bazi podataka
                var revocationInfo = _revocationDb.GetRevocationStatus(
                    certId.IssuerNameHash,
                    certId.IssuerKeyHash,
                    certId.SerialNumber
                );
 
                if (revocationInfo == null)
                {
                    // Nepoznat certifikat
                    responseBuilder.AddStatus(certId, OcspCertStatus.Unknown);
                }
                else if (revocationInfo.IsRevoked)
                {
                    // Opozvan
                    responseBuilder.AddStatus(
                        certId,
                        OcspCertStatus.Revoked,
                        revocationInfo.RevocationTime,
                        revocationInfo.RevocationReason
                    );
                }
                else
                {
                    // Valjan
                    responseBuilder.AddStatus(certId, OcspCertStatus.Good);
                }
            }
 
            // Potpisivanje odgovora
            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);
    }
}

Kreiranje certifikata za OCSP Responder

// OCSP Responder treba poseban certifikat
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,  // Kratka valjanost za 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 (bez provjere opoziva za 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} (za 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 Značenje
successful 200 Zahtjev obrađen
malformedRequest 200 Nevaljani zahtjev
internalError 200 Greška poslužitelja
tryLater 200 Privremeno nedostupno
sigRequired 200 Potreban potpis
unauthorized 200 Neovlašteno

Važno: OCSP uvijek vraća HTTP 200! Status je kodiran u odgovoru.


Zahtjevi za OCSP po industrijama

Industrija Response Caching Stapling Visoka dostupnost
WebPKI Maks. 10 min Obvezno 99.9%
Enterprise Maks. 1 sat Preporučeno Ovisno o SLA
Financijski sektor Maks. 5 min Obvezno 99.99%
Zdravstvo Maks. 1 sat Opcionalno 99.9%

Povezani scenariji

Odnos Scenarij Opis
Alternativa 6.1 Kreiranje CRL-a Offline provjera
Povezano 5.3 Provjera opoziva Provjera na strani klijenta
Preduvjet 6.4 Opoziv certifikata Proces opoziva

« ← 6.1 Kreiranje CRL-a | ↑ Pregled opoziva | 6.3 Delta-CRL → »


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