Scenario 11.2: Archiviazione chiavi

Categoria: Gestione chiavi
Complessità: Alta
Prerequisiti: Chiavi generate
Tempo stimato: 20-30 minuti


Descrizione

Questo scenario descrive l'archiviazione sicura delle chiavi crittografiche. La protezione delle chiavi private è essenziale - chiavi compromesse mettono a rischio tutti i certificati e le firme basati su di esse.

Opzioni di archiviazione:

Opzione Sicurezza Costi Applicazione
Filesystem (crittografato) Media Basso Sviluppo, Test
Windows DPAPI Buona Basso Windows Server
Azure Key Vault Alta Medio Cloud
HashiCorp Vault Alta Medio On-Premise/Cloud
HSM Molto alta Alto CA, Sistemi critici

Workflow

flowchart TD KEY[Chiave privata] --> ENCRYPT[Crittografare] ENCRYPT --> ACL[Controllo accessi] ACL --> STORE{Ubicazione} STORE --> FILE[Filesystem] STORE --> VAULT[Key Vault] STORE --> HSM[HSM] FILE --> BACKUP[Backup] VAULT --> BACKUP HSM --> BACKUP style ENCRYPT fill:#fff3e0 style ACL fill:#e8f5e9


Esempio codice: File crittografato (PEM)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Generare chiave
using var keyPair = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65);
 
// Salvare come PEM crittografato (PKCS#8)
keyPair.ToEncryptedPemFile(
    path: "private-key.pem",
    password: "StrongPassword123!",
    pbeAlgorithm: PbeAlgorithm.Aes256Cbc,
    prf: PbePrf.HmacSha256,
    iterations: 100000  // Iterazioni PBKDF2
);
 
// Chiave pubblica (non crittografata, condivisibile pubblicamente)
keyPair.PublicKey.ToPemFile("public-key.pem");
 
Console.WriteLine("Chiavi salvate:");
Console.WriteLine("  Privata: private-key.pem (crittografata AES-256-CBC)");
Console.WriteLine("  Pubblica: public-key.pem");

Esempio codice: Windows DPAPI

using System.Security.Cryptography;
 
public class DpapiKeyStorage
{
    private readonly string _keyStorePath;
 
    public DpapiKeyStorage(string basePath = null)
    {
        _keyStorePath = basePath ?? Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "WvdS", "Keys"
        );
        Directory.CreateDirectory(_keyStorePath);
    }
 
    public void StoreKey(string keyId, byte[] privateKey, DataProtectionScope scope = DataProtectionScope.CurrentUser)
    {
        // Crittografare con DPAPI
        var protectedData = ProtectedData.Protect(
            privateKey,
            entropy: Encoding.UTF8.GetBytes(keyId),  // Entropia aggiuntiva
            scope: scope
        );
 
        var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi");
        File.WriteAllBytes(keyPath, protectedData);
 
        // Impostare ACL (solo utente corrente)
        var fileSecurity = new FileSecurity();
        fileSecurity.SetAccessRuleProtection(true, false);  // Disabilitare ereditarietà
        fileSecurity.AddAccessRule(new FileSystemAccessRule(
            Environment.UserName,
            FileSystemRights.Read | FileSystemRights.Write,
            AccessControlType.Allow
        ));
 
        var fileInfo = new FileInfo(keyPath);
        fileInfo.SetAccessControl(fileSecurity);
 
        Console.WriteLine($"Chiave salvata con DPAPI: {keyPath}");
    }
 
    public byte[] LoadKey(string keyId, DataProtectionScope scope = DataProtectionScope.CurrentUser)
    {
        var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi");
 
        if (!File.Exists(keyPath))
        {
            throw new FileNotFoundException($"Chiave non trovata: {keyId}");
        }
 
        var protectedData = File.ReadAllBytes(keyPath);
 
        return ProtectedData.Unprotect(
            protectedData,
            entropy: Encoding.UTF8.GetBytes(keyId),
            scope: scope
        );
    }
 
    public void DeleteKey(string keyId)
    {
        var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi");
 
        if (File.Exists(keyPath))
        {
            // Cancellazione sicura: sovrascrivere prima di eliminare
            var fileLength = new FileInfo(keyPath).Length;
            using (var fs = new FileStream(keyPath, FileMode.Open, FileAccess.Write))
            {
                var zeros = new byte[fileLength];
                fs.Write(zeros, 0, zeros.Length);
                fs.Flush();
            }
 
            File.Delete(keyPath);
            Console.WriteLine($"Chiave eliminata: {keyId}");
        }
    }
}

Esempio codice: Azure Key Vault

using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;
using Azure.Identity;
 
public class AzureKeyVaultStorage
{
    private readonly KeyClient _keyClient;
    private readonly string _vaultUrl;
 
    public AzureKeyVaultStorage(string vaultUrl)
    {
        _vaultUrl = vaultUrl;
        _keyClient = new KeyClient(
            new Uri(vaultUrl),
            new DefaultAzureCredential()
        );
    }
 
    public async Task<string> StoreKeyAsync(string keyName, byte[] privateKeyPkcs8)
    {
        // Salvare chiave come Secret (per chiavi PQ)
        var secretClient = new SecretClient(
            new Uri(_vaultUrl),
            new DefaultAzureCredential()
        );
 
        var base64Key = Convert.ToBase64String(privateKeyPkcs8);
 
        await secretClient.SetSecretAsync(keyName, base64Key);
 
        Console.WriteLine($"Chiave salvata in Key Vault: {keyName}");
        return keyName;
    }
 
    public async Task<byte[]> LoadKeyAsync(string keyName)
    {
        var secretClient = new SecretClient(
            new Uri(_vaultUrl),
            new DefaultAzureCredential()
        );
 
        var secret = await secretClient.GetSecretAsync(keyName);
        return Convert.FromBase64String(secret.Value.Value);
    }
 
    public async Task RotateKeyAsync(string keyName, byte[] newPrivateKey)
    {
        // Creare nuovo secret (versionato)
        await StoreKeyAsync(keyName, newPrivateKey);
 
        // La vecchia versione rimane disponibile come backup
        Console.WriteLine($"Chiave ruotata: {keyName}");
    }
}

Esempio codice: HashiCorp Vault

using VaultSharp;
using VaultSharp.V1.AuthMethods.Token;
 
public class HashiCorpVaultStorage
{
    private readonly IVaultClient _vaultClient;
    private readonly string _mountPath;
 
    public HashiCorpVaultStorage(string vaultAddress, string token, string mountPath = "secret")
    {
        var authMethod = new TokenAuthMethodInfo(token);
        var settings = new VaultClientSettings(vaultAddress, authMethod);
        _vaultClient = new VaultClient(settings);
        _mountPath = mountPath;
    }
 
    public async Task StoreKeyAsync(string keyPath, byte[] privateKey, Dictionary<string, string> metadata = null)
    {
        var secretData = new Dictionary<string, object>
        {
            ["privateKey"] = Convert.ToBase64String(privateKey),
            ["algorithm"] = "ML-DSA-65",
            ["createdAt"] = DateTime.UtcNow.ToString("O")
        };
 
        if (metadata != null)
        {
            foreach (var (key, value) in metadata)
            {
                secretData[key] = value;
            }
        }
 
        await _vaultClient.V1.Secrets.KeyValue.V2.WriteSecretAsync(
            path: keyPath,
            data: secretData,
            mountPoint: _mountPath
        );
 
        Console.WriteLine($"Chiave salvata in Vault: {keyPath}");
    }
 
    public async Task<byte[]> LoadKeyAsync(string keyPath)
    {
        var secret = await _vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(
            path: keyPath,
            mountPoint: _mountPath
        );
 
        var base64Key = secret.Data.Data["privateKey"].ToString();
        return Convert.FromBase64String(base64Key);
    }
 
    public async Task<IEnumerable<string>> ListKeysAsync(string prefix = "")
    {
        var list = await _vaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync(
            path: prefix,
            mountPoint: _mountPath
        );
 
        return list.Data.Keys;
    }
}

Integrazione HSM (PKCS#11)

public class HsmKeyStorage
{
    private readonly string _pkcs11LibPath;
    private readonly string _tokenLabel;
 
    public HsmKeyStorage(string pkcs11LibPath, string tokenLabel)
    {
        _pkcs11LibPath = pkcs11LibPath;
        _tokenLabel = tokenLabel;
    }
 
    public void StoreKey(string keyLabel, byte[] privateKey, string userPin)
    {
        using var pkcs11Library = new Pkcs11Library(_pkcs11LibPath);
 
        var slot = pkcs11Library.GetSlotList(SlotsType.WithTokenPresent)
            .First(s => s.GetTokenInfo().Label.Trim() == _tokenLabel);
 
        using var session = slot.OpenSession(SessionType.ReadWrite);
        session.Login(CKU.CKU_USER, userPin);
 
        try
        {
            var keyAttributes = new List<ObjectAttribute>
            {
                new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY),
                new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_GENERIC_SECRET),
                new ObjectAttribute(CKA.CKA_TOKEN, true),
                new ObjectAttribute(CKA.CKA_PRIVATE, true),
                new ObjectAttribute(CKA.CKA_SENSITIVE, true),
                new ObjectAttribute(CKA.CKA_EXTRACTABLE, false),  // Non esportabile!
                new ObjectAttribute(CKA.CKA_LABEL, keyLabel),
                new ObjectAttribute(CKA.CKA_VALUE, privateKey)
            };
 
            session.CreateObject(keyAttributes);
            Console.WriteLine($"Chiave salvata in HSM: {keyLabel}");
        }
        finally
        {
            // Sovrascrivere dati chiave in memoria
            Array.Clear(privateKey, 0, privateKey.Length);
            session.Logout();
        }
    }
}

Requisiti di archiviazione specifici per settore

Settore Ubicazione Crittografia Controllo accessi
Sviluppo Filesystem PKCS#8 Permessi file
Enterprise Key Vault / Vault AES-256 RBAC
Finanza HSM (FIPS 140-3) Hardware Multi-persona
Sanità HSM o Vault AES-256 Audit-Log
CA/PKI HSM Hardware Quorum n-of-m

Scenari correlati

Relazione Scenario Descrizione
Prerequisito 11.1 Generazione chiavi Creare chiavi
Passo successivo 11.3 Rotazione chiavi Scambio regolare
Correlato 11.4 Backup chiavi Copie di sicurezza
Correlato 11.5 Distruzione chiavi Cancellazione sicura

« 11.1 Generazione chiavi | Panoramica chiavi | 11.3 Rotazione chiavi »


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

Zuletzt geändert: il 30/01/2026 alle 06:39