Szenario 4.3: Zertifikat-Archivierung

Kategorie: Zertifikate verwalten
Komplexität: ⭐⭐⭐ (Mittel)
Voraussetzungen: Abgelaufene/widerrufene Zertifikate
Geschätzte Zeit: 15-20 Minuten


Beschreibung

Dieses Szenario beschreibt die sichere Archivierung von Zertifikaten und zugehörigen Schlüsseln. Archivierung ist notwendig für:

Wichtig: Verschlüsselungsschlüssel müssen archiviert werden, wenn damit verschlüsselte Daten noch existieren. Signaturschlüssel sollten NICHT archiviert werden (nur Zertifikate).


Workflow

flowchart TD CERT[Zertifikat abgelaufen/widerrufen] --> CLASSIFY{Schlüsseltyp?} CLASSIFY -->|Verschlüsselung| ARCHIVE_KEY[Schlüssel archivieren] CLASSIFY -->|Signatur| DESTROY_KEY[Schlüssel vernichten] ARCHIVE_KEY --> ENCRYPT[Verschlüsselt speichern] DESTROY_KEY --> ARCHIVE_CERT[Nur Zertifikat archivieren] ENCRYPT --> STORE[Archiv-Storage] ARCHIVE_CERT --> STORE STORE --> LOG[Audit-Log] style ENCRYPT fill:#fff3e0 style STORE fill:#e8f5e9


Code-Beispiel (C#)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Zu archivierende Zertifikate laden
var expiredCert = ctx.LoadCertificate("old-server.crt.pem");
var expiredKey = ctx.LoadPrivateKey("old-server.key.pem", "OldPassword!");
 
// Archiv-Struktur
var archive = new CertificateArchive
{
    Certificate = expiredCert,
    ArchivedAt = DateTime.UtcNow,
    OriginalPath = "old-server.crt.pem",
    Reason = ArchiveReason.Expired,
    RetentionUntil = DateTime.UtcNow.AddYears(10),
    Metadata = new Dictionary<string, string>
    {
        ["Subject"] = expiredCert.Subject,
        ["SerialNumber"] = expiredCert.SerialNumber,
        ["NotBefore"] = expiredCert.NotBefore.ToString("O"),
        ["NotAfter"] = expiredCert.NotAfter.ToString("O"),
        ["Thumbprint"] = expiredCert.Thumbprint,
        ["KeyUsage"] = GetKeyUsageString(expiredCert)
    }
};
 
// Schlüssel nur bei Verschlüsselungszertifikaten archivieren
if (HasKeyEncipherment(expiredCert))
{
    // Schlüssel verschlüsselt speichern
    var encryptedKey = ctx.EncryptPrivateKey(
        expiredKey,
        archivePassword: "ArchivePassword!SecureVault2024",
        algorithm: PbeAlgorithm.Aes256Cbc,
        prf: PbePrf.HmacSha256,
        iterations: 100000
    );
    archive.EncryptedPrivateKey = encryptedKey;
}
 
// Als JSON archivieren
var archivePath = $"archive/{expiredCert.SerialNumber}.json";
File.WriteAllText(archivePath, JsonSerializer.Serialize(archive, new JsonSerializerOptions
{
    WriteIndented = true
}));
 
// Audit-Log
Console.WriteLine($"Archiviert: {expiredCert.Subject}");
Console.WriteLine($"  Serial: {expiredCert.SerialNumber}");
Console.WriteLine($"  Ablauf: {expiredCert.NotAfter:yyyy-MM-dd}");
Console.WriteLine($"  Archiv: {archivePath}");
Console.WriteLine($"  Aufbewahrung bis: {archive.RetentionUntil:yyyy-MM-dd}");

Archiv-Datenstruktur

public class CertificateArchive
{
    public string Version { get; } = "1.0";
    public DateTime ArchivedAt { get; set; }
    public X509Certificate2 Certificate { get; set; }
    public byte[]? EncryptedPrivateKey { get; set; }  // Nur bei Encryption-Certs
    public string OriginalPath { get; set; }
    public ArchiveReason Reason { get; set; }
    public DateTime RetentionUntil { get; set; }
    public Dictionary<string, string> Metadata { get; set; }
    public string? Notes { get; set; }
}
 
public enum ArchiveReason
{
    Expired,
    Revoked,
    Superseded,
    KeyCompromise,
    CessationOfOperation,
    PolicyChange
}

Branchenspezifische Aufbewahrungsfristen

Branche Vorschrift Aufbewahrungsfrist Schlüssel archivieren?
Finanzsektor GoBD 10 Jahre Ja (Verschlüsselung)
Healthcare DSGVO, PatArch 30 Jahre Ja (Patientendaten)
Energie EnWG, IT-SiG 10 Jahre Ja (Mess-Daten)
Öffentlich Archivgesetze 30+ Jahre Je nach Datentyp
Standard IT DSGVO 6 Jahre Nein (nur Zertifikat)

Archiv-Verzeichnis anlegen

// Strukturiertes Archiv-Verzeichnis
public class ArchiveDirectory
{
    private readonly string _basePath;
 
    public ArchiveDirectory(string basePath)
    {
        _basePath = basePath;
        Directory.CreateDirectory(Path.Combine(basePath, "certificates"));
        Directory.CreateDirectory(Path.Combine(basePath, "keys"));
        Directory.CreateDirectory(Path.Combine(basePath, "metadata"));
    }
 
    public void Archive(CertificateArchive archive)
    {
        var serial = archive.Certificate.SerialNumber;
        var year = archive.Certificate.NotAfter.Year;
 
        // Zertifikat (PEM)
        var certPath = Path.Combine(_basePath, "certificates", $"{year}", $"{serial}.crt.pem");
        Directory.CreateDirectory(Path.GetDirectoryName(certPath)!);
        archive.Certificate.ToPemFile(certPath);
 
        // Schlüssel (wenn vorhanden)
        if (archive.EncryptedPrivateKey != null)
        {
            var keyPath = Path.Combine(_basePath, "keys", $"{year}", $"{serial}.key.enc");
            Directory.CreateDirectory(Path.GetDirectoryName(keyPath)!);
            File.WriteAllBytes(keyPath, archive.EncryptedPrivateKey);
        }
 
        // Metadaten
        var metaPath = Path.Combine(_basePath, "metadata", $"{year}", $"{serial}.json");
        Directory.CreateDirectory(Path.GetDirectoryName(metaPath)!);
        File.WriteAllText(metaPath, JsonSerializer.Serialize(archive.Metadata));
    }
}

Archiv-Suche

// Archivierte Zertifikate durchsuchen
public IEnumerable<CertificateArchive> SearchArchive(
    string basePath,
    string? subjectFilter = null,
    DateTime? expiredAfter = null)
{
    var metadataFiles = Directory.GetFiles(
        Path.Combine(basePath, "metadata"),
        "*.json",
        SearchOption.AllDirectories
    );
 
    foreach (var file in metadataFiles)
    {
        var metadata = JsonSerializer.Deserialize<Dictionary<string, string>>(
            File.ReadAllText(file)
        );
 
        if (subjectFilter != null &&
            !metadata["Subject"].Contains(subjectFilter, StringComparison.OrdinalIgnoreCase))
            continue;
 
        if (expiredAfter.HasValue &&
            DateTime.Parse(metadata["NotAfter"]) < expiredAfter.Value)
            continue;
 
        var serial = Path.GetFileNameWithoutExtension(file);
        var year = Path.GetFileName(Path.GetDirectoryName(file));
 
        yield return LoadArchive(basePath, year, serial);
    }
}

Verwandte Szenarien

Beziehung Szenario Beschreibung
Voraussetzung 4.4 Backup Vor Archivierung sichern
Verwandt 11.5 Schlüsselvernichtung Signatur-Keys löschen
Verwandt 6.4 Widerrufen Vor Archivierung

« ← 4.2 Rekey | ↑ Verwaltung-Übersicht | 4.4 Backup → »


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