Scenarij 6.2: OCSP strežnik

Kategorija: Preklic (Revocation)
Kompleksnost: Visoka
Predpogoji: PKI infrastruktura, certifikat za podpisovanje OCSP
Ocenjeni čas: 30-45 minut


Opis

Ta scenarij opisuje implementacijo OCSP strežnika (Online Certificate Status Protocol, RFC 6960). OCSP omogoča preverjanje statusa certifikata v realnem času.

Prednosti pred CRL:


Potek dela

flowchart LR CLIENT[Odjemalec] -->|OCSP zahteva| RESPONDER[OCSP strežnik] RESPONDER --> DB[(Baza preklicov)] RESPONDER -->|OCSP odgovor| CLIENT subgraph Response GOOD[good] REVOKED[revoked] UNKNOWN[unknown] end style RESPONDER fill:#e8f5e9


Primer kode: Ustvarjanje OCSP zahteve

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Nalaganje certifikata za preverjanje in izdajatelja
var cert = ctx.LoadCertificate("server.crt.pem");
var issuer = ctx.LoadCertificate("intermediate-ca.crt.pem");
 
// Ustvarjanje OCSP zahteve
var ocspRequest = ctx.CreateOcspRequest(
    certificate: cert,
    issuer: issuer,
    hashAlgorithm: HashAlgorithmName.SHA256,
    nonce: true  // Zaščita pred ponovitvijo
);
 
// Zahteva kot DER
byte[] requestBytes = ocspRequest.Encode();
 
// Pošiljanje zahteve
using var http = new HttpClient();
var content = new ByteArrayContent(requestBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/ocsp-request");
 
var ocspUrl = ctx.GetOcspUrl(cert);  // Branje AIA Extension
var response = await http.PostAsync(ocspUrl, content);
 
var responseBytes = await response.Content.ReadAsByteArrayAsync();
 
// Razčlenjevanje odgovora
var status = ctx.ParseOcspResponse(responseBytes, cert, issuer);
 
Console.WriteLine($"OCSP Status: {status.Status}");
Console.WriteLine($"  Ustvarjeno: {status.ProducedAt}");
Console.WriteLine($"  This Update: {status.ThisUpdate}");
Console.WriteLine($"  Next Update: {status.NextUpdate}");

Primer kode: Implementacija OCSP strežnika

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
        {
            // Razčlenjevanje zahteve
            var request = _ctx.ParseOcspRequest(requestBytes);
 
            // Response Builder
            var responseBuilder = new OcspResponseBuilder();
 
            foreach (var certId in request.CertificateIds)
            {
                // Iskanje statusa v bazi podatkov
                var revocationInfo = _revocationDb.GetRevocationStatus(
                    certId.IssuerNameHash,
                    certId.IssuerKeyHash,
                    certId.SerialNumber
                );
 
                if (revocationInfo == null)
                {
                    // Neznan certifikat
                    responseBuilder.AddStatus(certId, OcspCertStatus.Unknown);
                }
                else if (revocationInfo.IsRevoked)
                {
                    // Preklican
                    responseBuilder.AddStatus(
                        certId,
                        OcspCertStatus.Revoked,
                        revocationInfo.RevocationTime,
                        revocationInfo.RevocationReason
                    );
                }
                else
                {
                    // Veljaven
                    responseBuilder.AddStatus(certId, OcspCertStatus.Good);
                }
            }
 
            // Podpisovanje 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);
    }
}

Ustvarjanje certifikata za OCSP strežnik

// OCSP strežnik potrebuje poseben 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 veljavnost za OCSP strežnik
    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 (brez preverjanja preklica za strežnik)
        .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");
    }
}

Status OCSP odgovora

Status HTTP Pomen
successful 200 Zahteva obdelana
malformedRequest 200 Neveljavna zahteva
internalError 200 Napaka strežnika
tryLater 200 Začasno nedostopno
sigRequired 200 Potreben podpis
unauthorized 200 Ni pooblastil

Pomembno: OCSP vedno vrne HTTP 200! Status je kodiran v odgovoru.


Panožne zahteve za OCSP

Panoga Predpomnjenje odgovorov Stapling Visoka razpoložljivost
WebPKI Maks. 10 min Obvezno 99.9%
Enterprise Maks. 1 ura Priporočeno Odvisno od SLA
Finančni sektor Maks. 5 min Obvezno 99.99%
Zdravstvo Maks. 1 ura Opcijsko 99.9%

Povezani scenariji

Povezava Scenarij Opis
Alternativa 6.1 Ustvarjanje CRL Preverjanje brez povezave
Povezano 5.3 Preverjanje preklica Preverjanje na strani odjemalca
Predpogoj 6.4 Preklic certifikata Postopek preklica

« ← 6.1 Ustvarjanje CRL | ↑ Pregled preklica | 6.3 Delta-CRL → »


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