Kategorie: Schlüsselmanagement
Komplexität: ⭐⭐⭐⭐ (Hoch)
Voraussetzungen: Schlüssel nicht mehr benötigt, Backups geprüft
Geschätzte Zeit: 20-30 Minuten
Dieses Szenario beschreibt die sichere und unwiderrufliche Vernichtung kryptographischer Schlüssel. Eine ordnungsgemäße Schlüsselvernichtung ist der letzte Schritt im Schlüssel-Lebenszyklus und kritisch für die Sicherheit.
Vernichtungs-Gründe:
WARNUNG: Schlüsselvernichtung ist unwiderruflich! Stellen Sie sicher:
using System.Security.Cryptography; using System.Runtime.InteropServices; public class SecureKeyDestruction { public void DestroyKeyInMemory(byte[] keyMaterial) { if (keyMaterial == null || keyMaterial.Length == 0) return; // 1. Mit Zufallsdaten überschreiben RandomNumberGenerator.Fill(keyMaterial); // 2. Mit Nullen überschreiben Array.Clear(keyMaterial, 0, keyMaterial.Length); // 3. Nochmals mit Zufallsdaten überschreiben RandomNumberGenerator.Fill(keyMaterial); // 4. Final mit Nullen überschreiben CryptographicOperations.ZeroMemory(keyMaterial); Console.WriteLine($"Speicher gelöscht: {keyMaterial.Length} Bytes"); } public void DestroyKeyOnDisk(string keyFilePath) { if (!File.Exists(keyFilePath)) { Console.WriteLine($"Datei nicht gefunden: {keyFilePath}"); return; } var fileInfo = new FileInfo(keyFilePath); var fileLength = fileInfo.Length; // Mehrfaches Überschreiben (DoD 5220.22-M Standard) using (var fs = new FileStream(keyFilePath, FileMode.Open, FileAccess.Write)) { var buffer = new byte[4096]; // Pass 1: Mit 0x00 überschreiben OverwriteFile(fs, fileLength, 0x00); // Pass 2: Mit 0xFF überschreiben OverwriteFile(fs, fileLength, 0xFF); // Pass 3: Mit Zufallsdaten überschreiben OverwriteFileRandom(fs, fileLength); // Pass 4: Nochmals mit 0x00 OverwriteFile(fs, fileLength, 0x00); fs.Flush(); } // Datei umbenennen (Metadaten verschleiern) var randomName = Path.Combine( Path.GetDirectoryName(keyFilePath), Guid.NewGuid().ToString() ); File.Move(keyFilePath, randomName); // Datei löschen File.Delete(randomName); Console.WriteLine($"Schlüsseldatei sicher gelöscht: {keyFilePath}"); } private void OverwriteFile(FileStream fs, long length, byte value) { fs.Seek(0, SeekOrigin.Begin); var buffer = new byte[4096]; Array.Fill(buffer, value); long remaining = length; while (remaining > 0) { int toWrite = (int)Math.Min(remaining, buffer.Length); fs.Write(buffer, 0, toWrite); remaining -= toWrite; } } private void OverwriteFileRandom(FileStream fs, long length) { fs.Seek(0, SeekOrigin.Begin); var buffer = new byte[4096]; long remaining = length; while (remaining > 0) { RandomNumberGenerator.Fill(buffer); int toWrite = (int)Math.Min(remaining, buffer.Length); fs.Write(buffer, 0, toWrite); remaining -= toWrite; } } }
public class KeyDestructionService { private readonly ILogger _logger; private readonly IAuditLog _auditLog; public async Task<DestructionCertificate> DestroyKeyAsync( string keyId, string reason, string authorizedBy, DestructionOptions options = null) { options ??= DestructionOptions.Default; var certificate = new DestructionCertificate { KeyId = keyId, DestructionId = Guid.NewGuid().ToString(), InitiatedAt = DateTime.UtcNow, Reason = reason, AuthorizedBy = authorizedBy }; try { // 1. Abhängigkeiten prüfen await ValidateDestructionAsync(keyId, certificate); // 2. Aktiven Schlüssel vernichten await DestroyPrimaryKeyAsync(keyId, certificate); // 3. Alle Backups vernichten if (options.DestroyBackups) { await DestroyBackupsAsync(keyId, certificate); } // 4. HSM-Schlüssel vernichten if (options.DestroyHsmKeys) { await DestroyHsmKeyAsync(keyId, certificate); } // 5. Audit-Log erstellen certificate.CompletedAt = DateTime.UtcNow; certificate.Status = DestructionStatus.Completed; await _auditLog.LogAsync(new AuditEntry { Action = "KEY_DESTRUCTION", KeyId = keyId, Details = certificate, Timestamp = DateTime.UtcNow }); _logger.LogInformation( "Schlüssel {KeyId} erfolgreich vernichtet. Zertifikat: {DestructionId}", keyId, certificate.DestructionId); return certificate; } catch (Exception ex) { certificate.Status = DestructionStatus.Failed; certificate.Error = ex.Message; _logger.LogError(ex, "Schlüsselvernichtung fehlgeschlagen: {KeyId}", keyId); throw; } } private async Task ValidateDestructionAsync(string keyId, DestructionCertificate cert) { // Prüfen ob noch aktive Zertifikate existieren var activeCerts = await _certStore.FindByKeyIdAsync(keyId); if (activeCerts.Any(c => c.NotAfter > DateTime.UtcNow)) { throw new InvalidOperationException( $"Schlüssel {keyId} wird noch von {activeCerts.Count} aktiven Zertifikaten verwendet"); } cert.ValidationsPassed.Add("Keine aktiven Zertifikate"); // Prüfen ob Aufbewahrungsfrist abgelaufen var metadata = await _keyStore.GetMetadataAsync(keyId); if (metadata.RetentionUntil > DateTime.UtcNow) { throw new InvalidOperationException( $"Aufbewahrungsfrist läuft bis {metadata.RetentionUntil:d}"); } cert.ValidationsPassed.Add("Aufbewahrungsfrist abgelaufen"); } private async Task DestroyPrimaryKeyAsync(string keyId, DestructionCertificate cert) { var keyPath = await _keyStore.GetPathAsync(keyId); var destruction = new SecureKeyDestruction(); // Schlüssel laden und Fingerprint für Zertifikat speichern var keyData = await File.ReadAllBytesAsync(keyPath); cert.KeyFingerprint = Convert.ToHexString(SHA256.HashData(keyData)).Substring(0, 32); // Sicher vernichten destruction.DestroyKeyOnDisk(keyPath); destruction.DestroyKeyInMemory(keyData); cert.PrimaryKeyDestroyed = true; cert.DestroyedLocations.Add(keyPath); } private async Task DestroyBackupsAsync(string keyId, DestructionCertificate cert) { var backupLocations = await _backupStore.FindBackupsAsync(keyId); var destruction = new SecureKeyDestruction(); foreach (var backupPath in backupLocations) { destruction.DestroyKeyOnDisk(backupPath); cert.DestroyedLocations.Add(backupPath); } cert.BackupsDestroyed = backupLocations.Count; } private async Task DestroyHsmKeyAsync(string keyId, DestructionCertificate cert) { // HSM-Key löschen (PKCS#11) using var pkcs11 = new Pkcs11Library(_hsmConfig.LibraryPath); var slot = pkcs11.GetSlotList(SlotsType.WithTokenPresent).First(); using var session = slot.OpenSession(SessionType.ReadWrite); session.Login(CKU.CKU_USER, _hsmConfig.UserPin); try { var key = session.FindAllObjects(new List<ObjectAttribute> { new ObjectAttribute(CKA.CKA_LABEL, keyId) }).FirstOrDefault(); if (key != null) { session.DestroyObject(key); cert.HsmKeyDestroyed = true; cert.DestroyedLocations.Add($"HSM:{keyId}"); } } finally { session.Logout(); } } } public class DestructionCertificate { public string DestructionId { get; set; } public string KeyId { get; set; } public string KeyFingerprint { get; set; } public DateTime InitiatedAt { get; set; } public DateTime? CompletedAt { get; set; } public string Reason { get; set; } public string AuthorizedBy { get; set; } public DestructionStatus Status { get; set; } public List<string> ValidationsPassed { get; set; } = new(); public bool PrimaryKeyDestroyed { get; set; } public int BackupsDestroyed { get; set; } public bool HsmKeyDestroyed { get; set; } public List<string> DestroyedLocations { get; set; } = new(); public string Error { get; set; } } public enum DestructionStatus { InProgress, Completed, Failed }
public class DpapiKeyDestruction { public void DestroyDpapiKey(string keyId) { var keyPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WvdS", "Keys", $"{keyId}.dpapi" ); if (!File.Exists(keyPath)) { Console.WriteLine($"DPAPI-Key nicht gefunden: {keyId}"); return; } var destruction = new SecureKeyDestruction(); // Datei sicher löschen destruction.DestroyKeyOnDisk(keyPath); // Auch evtl. Registry-Einträge entfernen try { using var key = Registry.CurrentUser.OpenSubKey( @"Software\WvdS\CryptoKeys", writable: true); key?.DeleteValue(keyId, throwOnMissingValue: false); } catch (Exception ex) { Console.WriteLine($"Registry-Bereinigung fehlgeschlagen: {ex.Message}"); } Console.WriteLine($"DPAPI-Key vernichtet: {keyId}"); } }
| Standard | Überschreibungen | Pattern | Anwendung |
|---|---|---|---|
| DoD 5220.22-M | 3 | 0x00, 0xFF, Random | US-Militär |
| NIST SP 800-88 | 1-3 | Abhängig von Medientyp | US-Regierung |
| BSI-TL 03423 | 3 | Random, 0x00, 0xFF | Deutsche Behörden |
| Gutmann | 35 | Verschiedene Pattern | Paranoid |
Für SSDs und Flash-Speicher: Sicheres Löschen erfordert spezielle Firmware-Befehle (TRIM, Secure Erase). Software-Überschreiben ist nicht zuverlässig!
| Branche | Anforderung | Zeugen | Dokumentation |
|---|---|---|---|
| Finanzsektor | PCI-DSS 3.6.6 | 2 Personen | Vernichtungsprotokoll |
| Healthcare | HIPAA | 1 Zeuge | Audit-Log |
| Behörden | VS-NfD | Geheimschutzbeauftragter | Vernichtungsbescheinigung |
| KRITIS | BSI | 2 Personen | Zertifikat + Protokoll |
| Beziehung | Szenario | Beschreibung |
|---|---|---|
| Voraussetzung | 11.4 Schlüssel-Backup | Alle Backups identifizieren |
| Voraussetzung | 11.3 Schlüssel-Rotation | Neue Keys vor Vernichtung |
| Verwandt | 6.4 Zertifikat widerrufen | Vor Vernichtung widerrufen |
| Verwandt | 4.3 Archivierung | Alternative: Archivieren statt Vernichten |
« ← 11.4 Schlüssel-Backup | ↑ Schlüssel-Übersicht | → Alle Szenarien »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional