~~NOTOC~~ ====== Scenarij 6.2: OCSP strežnik ====== **Kategorija:** [[.:start|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:** * Status v realnem času * Manjši odgovori * Brez popolnega prenosa * Zasebnost (preverjen samo en certifikat) ---- ===== 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** | [[.:crl_erstellen|6.1 Ustvarjanje CRL]] | Preverjanje brez povezave | | **Povezano** | [[sl:int:pqcrypt:szenarien:validierung:revocation_check|5.3 Preverjanje preklica]] | Preverjanje na strani odjemalca | | **Predpogoj** | [[.:zertifikat_widerrufen|6.4 Preklic certifikata]] | Postopek preklica | ---- << [[.:crl_erstellen|← 6.1 Ustvarjanje CRL]] | [[.:start|↑ Pregled preklica]] | [[.:delta_crl|6.3 Delta-CRL →]] >> {{tag>szenario widerruf ocsp responder echtzeit}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//