Szenario 5.3: Revocation Check

Kategorie: Validierung & Vertrauen
Komplexität: ⭐⭐⭐ (Mittel)
Voraussetzungen: Zertifikat mit CDP/OCSP
Geschätzte Zeit: 10-15 Minuten


Beschreibung

Dieses Szenario beschreibt die Prüfung des Widerrufsstatus eines Zertifikats mittels CRL (Certificate Revocation List) oder OCSP (Online Certificate Status Protocol).

Methoden:

Methode Vorteile Nachteile
CRL Offline-fähig, Batch Große Dateien, Verzögerung
OCSP Echtzeit, kleine Antworten Online-Abhängigkeit
OCSP Stapling Datenschutz, Performance Server-Konfiguration

Workflow

flowchart TD CERT[Zertifikat prüfen] --> HAS_OCSP{OCSP URL?} HAS_OCSP -->|Ja| OCSP[OCSP-Anfrage] HAS_OCSP -->|Nein| CRL[CRL herunterladen] OCSP --> OCSP_CHECK{Status?} CRL --> CRL_CHECK{In CRL?} OCSP_CHECK -->|good| OK[Nicht widerrufen] OCSP_CHECK -->|revoked| REVOKED[Widerrufen] OCSP_CHECK -->|unknown| CRL CRL_CHECK -->|Nein| OK CRL_CHECK -->|Ja| REVOKED style OK fill:#e8f5e9 style REVOKED fill:#ffebee


Code-Beispiel: OCSP Check

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography.X509Certificates;
 
using var ctx = PqCryptoContext.Initialize();
 
// Zertifikat laden
var cert = ctx.LoadCertificate("server.crt.pem");
var issuer = ctx.LoadCertificate("intermediate-ca.crt.pem");
 
// OCSP URL aus Zertifikat extrahieren
var ocspUrl = ctx.GetOcspUrl(cert);
 
if (!string.IsNullOrEmpty(ocspUrl))
{
    // OCSP-Request erstellen
    var ocspRequest = ctx.CreateOcspRequest(cert, issuer);
 
    // OCSP-Anfrage senden
    using var http = new HttpClient();
    http.DefaultRequestHeaders.Add("Content-Type", "application/ocsp-request");
 
    var response = await http.PostAsync(ocspUrl,
        new ByteArrayContent(ocspRequest));
 
    var ocspResponseBytes = await response.Content.ReadAsByteArrayAsync();
 
    // OCSP-Response parsen
    var status = ctx.ParseOcspResponse(ocspResponseBytes, cert, issuer);
 
    Console.WriteLine($"OCSP-Status: {status.Status}");
    Console.WriteLine($"  Produziert: {status.ProducedAt}");
    Console.WriteLine($"  Gültig bis: {status.NextUpdate}");
 
    if (status.Status == OcspStatus.Revoked)
    {
        Console.WriteLine($"  Widerrufen: {status.RevocationTime}");
        Console.WriteLine($"  Grund: {status.RevocationReason}");
    }
}

Code-Beispiel: CRL Check

public class CrlChecker
{
    private readonly Dictionary<string, CrlCache> _crlCache = new();
 
    public async Task<RevocationStatus> CheckCrl(X509Certificate2 cert, X509Certificate2 issuer)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // CDP URL aus Zertifikat extrahieren
        var cdpUrl = ctx.GetCrlDistributionPoint(cert);
        if (string.IsNullOrEmpty(cdpUrl))
        {
            return new RevocationStatus { Status = RevocationStatusCode.Unknown };
        }
 
        // CRL aus Cache oder herunterladen
        var crl = await GetOrDownloadCrl(cdpUrl, issuer);
 
        // Serial Number in CRL suchen
        var serialNumber = cert.SerialNumber;
        var entry = crl.Entries.FirstOrDefault(e =>
            e.SerialNumber.Equals(serialNumber, StringComparison.OrdinalIgnoreCase));
 
        if (entry != null)
        {
            return new RevocationStatus
            {
                Status = RevocationStatusCode.Revoked,
                RevocationTime = entry.RevocationDate,
                Reason = entry.Reason
            };
        }
 
        return new RevocationStatus
        {
            Status = RevocationStatusCode.Good,
            NextUpdate = crl.NextUpdate
        };
    }
 
    private async Task<ParsedCrl> GetOrDownloadCrl(string cdpUrl, X509Certificate2 issuer)
    {
        // Cache prüfen
        if (_crlCache.TryGetValue(cdpUrl, out var cached))
        {
            if (cached.NextUpdate > DateTime.UtcNow)
            {
                return cached.Crl;
            }
        }
 
        // CRL herunterladen
        using var http = new HttpClient();
        var crlBytes = await http.GetByteArrayAsync(cdpUrl);
 
        // CRL parsen und verifizieren
        using var ctx = PqCryptoContext.Initialize();
        var crl = ctx.ParseCrl(crlBytes);
 
        // Signatur prüfen
        if (!ctx.VerifyCrlSignature(crl, issuer))
        {
            throw new CryptographicException("CRL-Signatur ungültig");
        }
 
        // Cache aktualisieren
        _crlCache[cdpUrl] = new CrlCache
        {
            Crl = crl,
            NextUpdate = crl.NextUpdate
        };
 
        return crl;
    }
}

OCSP Stapling

public class OcspStapling
{
    // Server-seitig: OCSP-Response vorab holen
    public async Task<byte[]> GetStapledOcspResponse(
        X509Certificate2 serverCert,
        X509Certificate2 issuer)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        var ocspUrl = ctx.GetOcspUrl(serverCert);
        var request = ctx.CreateOcspRequest(serverCert, issuer);
 
        using var http = new HttpClient();
        var response = await http.PostAsync(ocspUrl,
            new ByteArrayContent(request));
 
        return await response.Content.ReadAsByteArrayAsync();
    }
 
    // Client-seitig: Gestapelte Response prüfen
    public OcspStatus ValidateStapledResponse(
        byte[] stapledResponse,
        X509Certificate2 serverCert,
        X509Certificate2 issuer)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        var status = ctx.ParseOcspResponse(stapledResponse, serverCert, issuer);
 
        // Freshness prüfen
        if (status.ProducedAt < DateTime.UtcNow.AddHours(-24))
        {
            throw new CryptographicException("OCSP-Response zu alt");
        }
 
        return status;
    }
}

.NET Chain-basierter Revocation Check

// Einfachste Methode: X509Chain nutzen
var chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(15);
 
// Optional: Nur CRL oder nur OCSP
// chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;  // Nur cached CRLs
 
bool isValid = chain.Build(cert);
 
// Revocation-Status aus Chain extrahieren
var revocationErrors = chain.ChainElements
    .SelectMany(e => e.ChainElementStatus)
    .Where(s => s.Status.HasFlag(X509ChainStatusFlags.Revoked) ||
                s.Status.HasFlag(X509ChainStatusFlags.RevocationStatusUnknown))
    .ToList();
 
if (revocationErrors.Any())
{
    foreach (var error in revocationErrors)
    {
        Console.WriteLine($"Revocation-Problem: {error.StatusInformation}");
    }
}

Branchenspezifische Anforderungen

Branche Methode Max. Verzögerung Fallback
WebPKI OCSP Must-Staple Echtzeit Soft-Fail
Finanzsektor OCSP + CRL 4 Stunden Hard-Fail
Healthcare CRL 24 Stunden Soft-Fail
Energie/SCADA CRL (Offline) 7 Tage Hard-Fail

Soft-Fail vs Hard-Fail:

  • Soft-Fail: Bei Nichterreichbarkeit → Zertifikat akzeptieren
  • Hard-Fail: Bei Nichterreichbarkeit → Zertifikat ablehnen

Verwandte Szenarien

Beziehung Szenario Beschreibung
Voraussetzung 5.2 Chain Validation Chain aufbauen
Verwandt 6.1 CRL erstellen CRL generieren
Verwandt 6.2 OCSP Responder OCSP betreiben

« ← 5.2 Chain Validation | ↑ Validierung-Übersicht | 5.4 Policy Validation → »


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

Zuletzt geändert: den 29.01.2026 um 15:13