Scenario 4.3: Archiviazione certificati

Categoria: Gestire certificati
Complessità: Media
Prerequisiti: Certificati scaduti/revocati
Tempo stimato: 15-20 minuti


Descrizione

Questo scenario descrive l'archiviazione sicura di certificati e chiavi associate. L'archiviazione è necessaria per:

  • Obblighi di conservazione compliance (GDPR, GoBD, NIS2)
  • Tracciabilità forense
  • Decrittazione dati storici
  • Evidenze per audit

Importante: Le chiavi di crittografia devono essere archiviate se esistono ancora dati cifrati con esse. Le chiavi di firma NON dovrebbero essere archiviate (solo i certificati).


Workflow

flowchart TD CERT[Certificato scaduto/revocato] --> CLASSIFY{Tipo chiave?} CLASSIFY -->|Crittografia| ARCHIVE_KEY[Archiviare chiave] CLASSIFY -->|Firma| DESTROY_KEY[Distruggere chiave] ARCHIVE_KEY --> ENCRYPT[Salvare cifrato] DESTROY_KEY --> ARCHIVE_CERT[Archiviare solo certificato] ENCRYPT --> STORE[Storage archivio] ARCHIVE_CERT --> STORE STORE --> LOG[Log audit] style ENCRYPT fill:#fff3e0 style STORE fill:#e8f5e9


Esempio codice (C#)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Caricare certificati da archiviare
var expiredCert = ctx.LoadCertificate("old-server.crt.pem");
var expiredKey = ctx.LoadPrivateKey("old-server.key.pem", "OldPassword!");
 
// Struttura archivio
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)
    }
};
 
// Archiviare chiave solo per certificati di crittografia
if (HasKeyEncipherment(expiredCert))
{
    // Salvare chiave cifrata
    var encryptedKey = ctx.EncryptPrivateKey(
        expiredKey,
        archivePassword: "ArchivePassword!SecureVault2024",
        algorithm: PbeAlgorithm.Aes256Cbc,
        prf: PbePrf.HmacSha256,
        iterations: 100000
    );
    archive.EncryptedPrivateKey = encryptedKey;
}
 
// Archiviare come JSON
var archivePath = $"archive/{expiredCert.SerialNumber}.json";
File.WriteAllText(archivePath, JsonSerializer.Serialize(archive, new JsonSerializerOptions
{
    WriteIndented = true
}));
 
// Log audit
Console.WriteLine($"Archiviato: {expiredCert.Subject}");
Console.WriteLine($"  Serial: {expiredCert.SerialNumber}");
Console.WriteLine($"  Scadenza: {expiredCert.NotAfter:yyyy-MM-dd}");
Console.WriteLine($"  Archivio: {archivePath}");
Console.WriteLine($"  Conservazione fino a: {archive.RetentionUntil:yyyy-MM-dd}");

Struttura dati archivio

public class CertificateArchive
{
    public string Version { get; } = "1.0";
    public DateTime ArchivedAt { get; set; }
    public X509Certificate2 Certificate { get; set; }
    public byte[]? EncryptedPrivateKey { get; set; }  // Solo per cert di crittografia
    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
}

Periodi di conservazione specifici per settore

Settore Normativa Periodo conservazione Archiviare chiave?
Settore finanziario GoBD 10 anni Si (crittografia)
Sanità GDPR, PatArch 30 anni Si (dati pazienti)
Energia EnWG, IT-SiG 10 anni Si (dati misurazione)
Pubblica Amministrazione Leggi archivi 30+ anni Secondo tipo dati
IT standard GDPR 6 anni No (solo certificato)

Creare directory archivio

// Directory archivio strutturata
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;
 
        // Certificato (PEM)
        var certPath = Path.Combine(_basePath, "certificates", $"{year}", $"{serial}.crt.pem");
        Directory.CreateDirectory(Path.GetDirectoryName(certPath)!);
        archive.Certificate.ToPemFile(certPath);
 
        // Chiave (se presente)
        if (archive.EncryptedPrivateKey != null)
        {
            var keyPath = Path.Combine(_basePath, "keys", $"{year}", $"{serial}.key.enc");
            Directory.CreateDirectory(Path.GetDirectoryName(keyPath)!);
            File.WriteAllBytes(keyPath, archive.EncryptedPrivateKey);
        }
 
        // Metadati
        var metaPath = Path.Combine(_basePath, "metadata", $"{year}", $"{serial}.json");
        Directory.CreateDirectory(Path.GetDirectoryName(metaPath)!);
        File.WriteAllText(metaPath, JsonSerializer.Serialize(archive.Metadata));
    }
}

Ricerca nell'archivio

// Cercare certificati archiviati
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);
    }
}

Scenari correlati

Relazione Scenario Descrizione
Prerequisito 4.4 Backup Salvare prima dell'archiviazione
Correlato 11.5 Distruzione chiavi Eliminare chiavi firma
Correlato 6.4 Revocare Prima dell'archiviazione

« ← 4.2 Rekey | ↑ Panoramica gestione | 4.4 Backup → »


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

Zuletzt geändert: il 30/01/2026 alle 07:11