~~NOTOC~~
====== Scenario 11.2: Key Storage ======
**Category:** [[.:start|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 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 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 metadata = null)
{
var secretData = new Dictionary
{
["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 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> 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
{
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 |
----
===== Related Scenarios =====
^ Relationship ^ Scenario ^ Description ^
| **Prerequisite** | [[.:generierung|11.1 Key Generation]] | Create keys |
| **Next Step** | [[.:rotation|11.3 Key Rotation]] | Regular exchange |
| **Related** | [[.:backup|11.4 Key Backup]] | Backup copies |
| **Related** | [[.:vernichtung|11.5 Key Destruction]] | Secure deletion |
----
<< [[.:generierung|← 11.1 Key Generation]] | [[.:start|↑ Key Overview]] | [[.:rotation|11.3 Key Rotation →]] >>
{{tag>scenario key storage dpapi vault hsm}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//