~~NOTOC~~
====== Szenario 6.2: OCSP Responder ======
**Kategorie:** [[.:start|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:**
* Echtzeit-Status
* Kleinere Antworten
* Kein vollständiger Download
* Privacy (nur ein Zertifikat geprüft)
----
===== 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** | [[.:crl_erstellen|6.1 CRL erstellen]] | Offline-Prüfung |
| **Verwandt** | [[de:int:pqcrypt:szenarien:validierung:revocation_check|5.3 Revocation Check]] | Client-seitige Prüfung |
| **Voraussetzung** | [[.:zertifikat_widerrufen|6.4 Zertifikat widerrufen]] | Widerrufsprozess |
----
<< [[.:crl_erstellen|← 6.1 CRL erstellen]] | [[.:start|↑ Widerruf-Übersicht]] | [[.: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//