~~NOTOC~~ ====== Szenario 6.4: Zertifikat widerrufen ====== **Kategorie:** [[.:start|Widerruf (Revocation)]] \\ **Komplexität:** ⭐⭐⭐ (Mittel) \\ **Voraussetzungen:** Zertifikat und CA-Zugriff \\ **Geschätzte Zeit:** 10-15 Minuten ---- ===== Beschreibung ===== Dieses Szenario beschreibt den **vollständigen Prozess zum Widerruf eines Zertifikats**. Ein Widerruf ist notwendig bei: * **Schlüsselkompromittierung** - Schlüssel gestohlen/verloren * **Ablösung** - Neues Zertifikat ausgestellt * **Betriebseinstellung** - Dienst nicht mehr aktiv * **Zugehörigkeitsänderung** - Mitarbeiter verlässt Organisation * **Policy-Verletzung** - Zertifikat entspricht nicht mehr Richtlinien ---- ===== Workflow ===== flowchart TD REQUEST[Widerrufsantrag] --> VERIFY[Berechtigung prüfen] VERIFY --> REASON[Grund dokumentieren] REASON --> DB[In Revocation-DB eintragen] DB --> CRL[CRL aktualisieren] DB --> OCSP[OCSP aktualisieren] CRL --> PUBLISH[Veröffentlichen] OCSP --> PUBLISH PUBLISH --> NOTIFY[Stakeholder benachrichtigen] NOTIFY --> ARCHIVE[Dokumentation archivieren] style REQUEST fill:#ffebee style PUBLISH fill:#e8f5e9 ---- ===== Code-Beispiel (C#) ===== using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using var ctx = PqCryptoContext.Initialize(); // Zu widerrufendes Zertifikat laden var certToRevoke = ctx.LoadCertificate("server.crt.pem"); // CA-Zugriff var caCert = ctx.LoadCertificate("intermediate-ca.crt.pem"); var caKey = ctx.LoadPrivateKey("intermediate-ca.key.pem", "CaPassword!"); // Widerruf dokumentieren var revocationRecord = new RevocationRecord { SerialNumber = certToRevoke.SerialNumber, Subject = certToRevoke.Subject, RevocationTime = DateTimeOffset.UtcNow, Reason = X509RevocationReason.KeyCompromise, RequestedBy = "admin@example.com", RequestedAt = DateTimeOffset.UtcNow, ApprovedBy = "security@example.com", Notes = "Private Key möglicherweise in Git Repository exponiert" }; // In Datenbank speichern var revocationDb = new RevocationDatabase("revocations.db"); revocationDb.AddRevocation(revocationRecord); Console.WriteLine($"Zertifikat widerrufen:"); Console.WriteLine($" Serial: {revocationRecord.SerialNumber}"); Console.WriteLine($" Subject: {revocationRecord.Subject}"); Console.WriteLine($" Grund: {revocationRecord.Reason}"); Console.WriteLine($" Zeit: {revocationRecord.RevocationTime:yyyy-MM-dd HH:mm:ss} UTC"); ---- ===== CRL und OCSP aktualisieren ===== public class RevocationManager { private readonly RevocationDatabase _db; private readonly X509Certificate2 _caCert; private readonly AsymmetricAlgorithm _caKey; public async Task RevokeAndPublish( X509Certificate2 certificate, X509RevocationReason reason, string requestedBy) { using var ctx = PqCryptoContext.Initialize(); // 1. In Datenbank eintragen var record = new RevocationRecord { SerialNumber = certificate.SerialNumber, Subject = certificate.Subject, RevocationTime = DateTimeOffset.UtcNow, Reason = reason, RequestedBy = requestedBy }; _db.AddRevocation(record); // 2. Neue CRL generieren var crlBuilder = new CertificateRevocationListBuilder(); foreach (var rev in _db.GetAllRevocations()) { crlBuilder.AddEntry( HexToBytes(rev.SerialNumber), rev.RevocationTime, rev.Reason ); } var nextCrlNumber = _db.GetNextCrlNumber(); var crlBytes = crlBuilder.Build( _caCert, nextCrlNumber, DateTimeOffset.UtcNow.AddDays(7), HashAlgorithmName.SHA256, mode: CryptoMode.Hybrid ); // 3. CRL veröffentlichen await PublishCrl(crlBytes); // 4. OCSP Cache invalidieren (falls Stapling) InvalidateOcspCache(certificate.SerialNumber); // 5. Benachrichtigung senden await NotifyStakeholders(record); } private async Task PublishCrl(byte[] crlBytes) { // Lokal speichern File.WriteAllBytes("current.crl", crlBytes); // Auf CDP hochladen using var http = new HttpClient(); var content = new ByteArrayContent(crlBytes); await http.PutAsync("https://crl.example.com/intermediate.crl", content); Console.WriteLine("CRL veröffentlicht"); } private async Task NotifyStakeholders(RevocationRecord record) { // E-Mail an Zertifikatsinhaber // E-Mail an Security-Team // Ticket erstellen Console.WriteLine($"Benachrichtigungen gesendet für {record.SerialNumber}"); } } ---- ===== Widerrufsgründe und Verwendung ===== ^ Code ^ Reason ^ Wann verwenden ^ Urgenz ^ | 0 | unspecified | Kein spezifischer Grund | Normal | | 1 | keyCompromise | Schlüssel kompromittiert/gestohlen | **KRITISCH** | | 2 | cACompromise | CA kompromittiert | **KRITISCH** | | 3 | affiliationChanged | Organisation gewechselt | Normal | | 4 | superseded | Neues Zertifikat ausgestellt | Normal | | 5 | cessationOfOperation | Dienst eingestellt | Normal | | 6 | certificateHold | Temporär gesperrt | Niedrig | | 9 | privilegeWithdrawn | Berechtigung entzogen | Normal | **keyCompromise (1):** Sofortige Maßnahmen erforderlich! * CRL sofort aktualisieren * Alle betroffenen Systeme informieren * Neues Zertifikat ausstellen * Forensische Analyse starten ---- ===== Certificate Hold und Aufhebung ===== public class CertificateHoldManager { // Zertifikat temporär sperren public void PlaceOnHold(string serialNumber, string reason) { _db.AddRevocation(new RevocationRecord { SerialNumber = serialNumber, RevocationTime = DateTimeOffset.UtcNow, Reason = X509RevocationReason.CertificateHold, Notes = reason, IsOnHold = true }); RegenerateCrl(); } // Hold aufheben public void RemoveFromHold(string serialNumber, string reason) { var record = _db.GetRevocation(serialNumber); if (record?.Reason != X509RevocationReason.CertificateHold) { throw new InvalidOperationException("Nur Hold-Zertifikate können freigegeben werden"); } // In Delta-CRL mit RemoveFromCrl markieren _db.MarkAsRemovedFromHold(serialNumber); // Oder aus Basis-CRL entfernen _db.DeleteRevocation(serialNumber); RegenerateCrl(); } } ---- ===== Audit-Trail ===== public class RevocationAudit { public void LogRevocation(RevocationRecord record) { var auditEntry = new { Timestamp = DateTimeOffset.UtcNow, Action = "CERTIFICATE_REVOKED", SerialNumber = record.SerialNumber, Subject = record.Subject, Reason = record.Reason.ToString(), RequestedBy = record.RequestedBy, ApprovedBy = record.ApprovedBy, Notes = record.Notes, ClientIp = GetClientIp(), UserAgent = GetUserAgent() }; // In Audit-Log schreiben var json = JsonSerializer.Serialize(auditEntry); File.AppendAllText("audit.log", json + Environment.NewLine); // SIEM benachrichtigen SendToSiem(auditEntry); } } ---- ===== Branchenspezifische Widerrufsanforderungen ===== ^ Branche ^ Max. Propagationszeit ^ Vier-Augen ^ Audit ^ | **WebPKI** | 24 Stunden | Optional | Pflicht | | **Finanzsektor** | 4 Stunden | Pflicht | Pflicht | | **Healthcare** | 24 Stunden | Pflicht | 10 Jahre | | **Energie/SCADA** | 7 Tage | Pflicht | Pflicht | | **Government** | 24 Stunden | Pflicht | 30 Jahre | ---- ===== Verwandte Szenarien ===== ^ Beziehung ^ Szenario ^ Beschreibung ^ | **Nächster Schritt** | [[.:crl_erstellen|6.1 CRL erstellen]] | CRL aktualisieren | | **Nächster Schritt** | [[.:ocsp_responder|6.2 OCSP Responder]] | Status bereitstellen | | **Verwandt** | [[de:int:pqcrypt:szenarien:verwaltung:rekey|4.2 Rekey]] | Neues Zertifikat | ---- << [[.:delta_crl|← 6.3 Delta-CRL]] | [[.:start|↑ Widerruf-Übersicht]] | [[de:int:pqcrypt:szenarien:start|→ Alle Szenarien]] >> {{tag>szenario widerruf revocation prozess audit}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//