Scenario 11.2: Key Storage

Category: Key Management
Complexity: ⭐⭐⭐⭐ (High)
Prerequisites: Generated keys
Estimated Time: 20-30 Minutes


Description

This scenario describes the secure storage of cryptographic keys. Protection of private keys is essential - compromised keys endanger all certificates and signatures based on them.

Storage Options:

Option Security Cost Application
File System (encrypted) Medium Low Development, Test
Windows DPAPI Good Low Windows Server
Azure Key Vault High Medium Cloud
HashiCorp Vault High Medium On-Premise/Cloud
HSM Very High High CA, Critical Systems

Workflow

flowchart TD KEY[Private Key] --> ENCRYPT[Encrypt] ENCRYPT --> ACL[Access Control] ACL --> STORE{Storage Location} STORE --> FILE[File System] STORE --> VAULT[Key Vault] STORE --> HSM[HSM] FILE --> BACKUP[Backup] VAULT --> BACKUP HSM --> BACKUP style ENCRYPT fill:#fff3e0 style ACL fill:#e8f5e9


Code Example: Encrypted File (PEM)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Generate key
using var keyPair = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65);
 
// Save as encrypted PEM (PKCS#8)
keyPair.ToEncryptedPemFile(
    path: "private-key.pem",
    password: "StrongPassword123!",
    pbeAlgorithm: PbeAlgorithm.Aes256Cbc,
    prf: PbePrf.HmacSha256,
    iterations: 100000  // PBKDF2 iterations
);
 
// Public key (unencrypted, shareable)
keyPair.PublicKey.ToPemFile("public-key.pem");
 
Console.WriteLine("Keys saved:");
Console.WriteLine("  Private: private-key.pem (AES-256-CBC encrypted)");
Console.WriteLine("  Public: public-key.pem");

Code Example: 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)
    {
        // Encrypt with DPAPI
        var protectedData = ProtectedData.Protect(
            privateKey,
            entropy: Encoding.UTF8.GetBytes(keyId),  // Additional entropy
            scope: scope
        );
 
        var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi");
        File.WriteAllBytes(keyPath, protectedData);
 
        // Set ACL (current user only)
        var fileSecurity = new FileSecurity();
        fileSecurity.SetAccessRuleProtection(true, false);  // Disable inheritance
        fileSecurity.AddAccessRule(new FileSystemAccessRule(
            Environment.UserName,
            FileSystemRights.Read | FileSystemRights.Write,
            AccessControlType.Allow
        ));
 
        var fileInfo = new FileInfo(keyPath);
        fileInfo.SetAccessControl(fileSecurity);
 
        Console.WriteLine($"Key stored with 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($"Key not found: {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))
        {
            // Secure deletion: Overwrite before delete
            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($"Key deleted: {keyId}");
        }
    }
}

Code Example: 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)
    {
        // Store key as secret (for PQ keys)
        var secretClient = new SecretClient(
            new Uri(_vaultUrl),
            new DefaultAzureCredential()
        );
 
        var base64Key = Convert.ToBase64String(privateKeyPkcs8);
 
        await secretClient.SetSecretAsync(keyName, base64Key);
 
        Console.WriteLine($"Key stored 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)
    {
        // Create new secret (versioned)
        await StoreKeyAsync(keyName, newPrivateKey);
 
        // Old version remains available as backup
        Console.WriteLine($"Key rotated: {keyName}");
    }
}

Code Example: 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($"Key stored 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;
    }
}

HSM Integration (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),  // Not exportable!
                new ObjectAttribute(CKA.CKA_LABEL, keyLabel),
                new ObjectAttribute(CKA.CKA_VALUE, privateKey)
            };
 
            session.CreateObject(keyAttributes);
            Console.WriteLine($"Key stored in HSM: {keyLabel}");
        }
        finally
        {
            // Overwrite key data in memory
            Array.Clear(privateKey, 0, privateKey.Length);
            session.Logout();
        }
    }
}

Industry-Specific Storage Requirements

Industry Storage Location Encryption Access Control
Development File System PKCS#8 File Permissions
Enterprise Key Vault / Vault AES-256 RBAC
Financial Sector HSM (FIPS 140-3) Hardware Multi-Person
Healthcare HSM or Vault AES-256 Audit Log
CA/PKI HSM Hardware n-of-m Quorum

Relationship Scenario Description
Prerequisite 11.1 Key Generation Create keys
Next Step 11.3 Key Rotation Regular exchange
Related 11.4 Key Backup Backup copies
Related 11.5 Key Destruction Secure deletion

« ← 11.1 Key Generation | ↑ Key Overview | 11.3 Key Rotation → »


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