Szenario 11.5: Schlüsselvernichtung

Kategorie: Schlüsselmanagement
Komplexität: ⭐⭐⭐⭐ (Hoch)
Voraussetzungen: Schlüssel nicht mehr benötigt, Backups geprüft
Geschätzte Zeit: 20-30 Minuten


Beschreibung

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:

  • Alle verschlüsselten Daten sind entschlüsselt oder nicht mehr benötigt
  • Alle Signaturen sind validiert oder nicht mehr relevant
  • Backups wurden ebenfalls vernichtet

Workflow

flowchart TD INIT[Vernichtung initiieren] --> CHECK{Prüfungen} CHECK --> DEP[Abhängigkeiten prüfen] DEP --> BAK[Backups lokalisieren] BAK --> APPROVE{Genehmigung?} APPROVE -->|Nein| ABORT[Abbruch] APPROVE -->|Ja| DESTROY[Schlüssel überschreiben] DESTROY --> BACKUP_DEL[Backups vernichten] BACKUP_DEL --> HSM_DEL[HSM-Keys löschen] HSM_DEL --> LOG[Audit-Log] LOG --> CERT[Bescheinigung] style DESTROY fill:#ffebee style BACKUP_DEL fill:#ffebee style HSM_DEL fill:#ffebee


Code-Beispiel: Sichere Speicher-Löschung

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;
        }
    }
}

Code-Beispiel: Vollständige Vernichtung mit Protokoll

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
}

Code-Beispiel: DPAPI-Schlüssel vernichten

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}");
    }
}

Vernichtungsstandards

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!


Branchenspezifische Vernichtungsanforderungen

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

Verwandte Szenarien

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