~~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//