Scenarij 4.3: Arhiviranje certifikata

Kategorija: Upravljanje certifikatima
Složenost: ⭐⭐⭐ (Srednja)
Preduvjeti: Istekli/opozvani certifikati
Procijenjeno vrijeme: 15-20 minuta


Opis

Ovaj scenarij opisuje sigurno arhiviranje certifikata i pripadajućih ključeva. Arhiviranje je potrebno za:

  • Obveze čuvanja sukladno propisima (GDPR, GoBD, NIS2)
  • Forenzičku sljedivost
  • Dešifriranje povijesnih podataka
  • Dokaze za reviziju

Važno: Ključevi za šifriranje moraju se arhivirati ako još uvijek postoje šifrirani podaci. Ključevi za potpis NE bi se trebali arhivirati (samo certifikati).


Tijek rada

flowchart TD CERT[Certifikat istekao/opozvan] --> CLASSIFY{Tip ključa?} CLASSIFY -->|Šifriranje| ARCHIVE_KEY[Arhivirati ključ] CLASSIFY -->|Potpis| DESTROY_KEY[Uništiti ključ] ARCHIVE_KEY --> ENCRYPT[Šifrirano spremiti] DESTROY_KEY --> ARCHIVE_CERT[Arhivirati samo certifikat] ENCRYPT --> STORE[Arhivsko spremište] ARCHIVE_CERT --> STORE STORE --> LOG[Audit log] style ENCRYPT fill:#fff3e0 style STORE fill:#e8f5e9


Primjer koda (C#)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Učitavanje certifikata za arhiviranje
var expiredCert = ctx.LoadCertificate("old-server.crt.pem");
var expiredKey = ctx.LoadPrivateKey("old-server.key.pem", "OldPassword!");
 
// Struktura arhiva
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)
    }
};
 
// Arhivirati ključ samo kod certifikata za šifriranje
if (HasKeyEncipherment(expiredCert))
{
    // Šifrirano spremiti ključ
    var encryptedKey = ctx.EncryptPrivateKey(
        expiredKey,
        archivePassword: "ArchivePassword!SecureVault2024",
        algorithm: PbeAlgorithm.Aes256Cbc,
        prf: PbePrf.HmacSha256,
        iterations: 100000
    );
    archive.EncryptedPrivateKey = encryptedKey;
}
 
// Arhivirati kao JSON
var archivePath = $"archive/{expiredCert.SerialNumber}.json";
File.WriteAllText(archivePath, JsonSerializer.Serialize(archive, new JsonSerializerOptions
{
    WriteIndented = true
}));
 
// Audit log
Console.WriteLine($"Arhivirano: {expiredCert.Subject}");
Console.WriteLine($"  Serial: {expiredCert.SerialNumber}");
Console.WriteLine($"  Istek: {expiredCert.NotAfter:yyyy-MM-dd}");
Console.WriteLine($"  Arhiv: {archivePath}");
Console.WriteLine($"  Čuvanje do: {archive.RetentionUntil:yyyy-MM-dd}");

Struktura podataka arhiva

public class CertificateArchive
{
    public string Version { get; } = "1.0";
    public DateTime ArchivedAt { get; set; }
    public X509Certificate2 Certificate { get; set; }
    public byte[]? EncryptedPrivateKey { get; set; }  // Samo kod Encryption-certifikata
    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
}

Rokovi čuvanja po industrijama

Industrija Propis Rok čuvanja Arhivirati ključ?
Financijski sektor GoBD 10 godina Da (šifriranje)
Zdravstvo GDPR, PatArch 30 godina Da (podaci pacijenata)
Energetika EnWG, IT-SiG 10 godina Da (mjerni podaci)
Javni sektor Zakoni o arhiviranju 30+ godina Ovisno o tipu podataka
Standardni IT GDPR 6 godina Ne (samo certifikat)

Kreiranje direktorija arhiva

// Strukturirani direktorij arhiva
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;
 
        // Certifikat (PEM)
        var certPath = Path.Combine(_basePath, "certificates", $"{year}", $"{serial}.crt.pem");
        Directory.CreateDirectory(Path.GetDirectoryName(certPath)!);
        archive.Certificate.ToPemFile(certPath);
 
        // Ključ (ako postoji)
        if (archive.EncryptedPrivateKey != null)
        {
            var keyPath = Path.Combine(_basePath, "keys", $"{year}", $"{serial}.key.enc");
            Directory.CreateDirectory(Path.GetDirectoryName(keyPath)!);
            File.WriteAllBytes(keyPath, archive.EncryptedPrivateKey);
        }
 
        // Metapodaci
        var metaPath = Path.Combine(_basePath, "metadata", $"{year}", $"{serial}.json");
        Directory.CreateDirectory(Path.GetDirectoryName(metaPath)!);
        File.WriteAllText(metaPath, JsonSerializer.Serialize(archive.Metadata));
    }
}

Pretraživanje arhiva

// Pretraživanje arhiviranih certifikata
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);
    }
}

Povezani scenariji

Odnos Scenarij Opis
Preduvjet 4.4 Sigurnosna kopija Osigurati prije arhiviranja
Povezano 11.5 Uništavanje ključeva Brisanje potpisnih ključeva
Povezano 6.4 Opoziv Prije arhiviranja

« ← 4.2 Re-Key | ↑ Pregled upravljanja | 4.4 Sigurnosna kopija → »


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

Zuletzt geändert: 30.01.2026. u 07:27