Kategorie: Schlüsselmanagement
Komplexität: ⭐⭐⭐⭐ (Hoch)
Voraussetzungen: Generierte Schlüssel
Geschätzte Zeit: 20-30 Minuten
Dieses Szenario beschreibt die sichere Speicherung kryptographischer Schlüssel. Der Schutz privater Schlüssel ist essenziell - kompromittierte Schlüssel gefährden alle darauf basierenden Zertifikate und Signaturen.
Speicheroptionen:
| Option | Sicherheit | Kosten | Anwendung |
|---|---|---|---|
| Dateisystem (verschlüsselt) | Mittel | Niedrig | Entwicklung, Test |
| Windows DPAPI | Gut | Niedrig | Windows Server |
| Azure Key Vault | Hoch | Mittel | Cloud |
| HashiCorp Vault | Hoch | Mittel | On-Premise/Cloud |
| HSM | Sehr Hoch | Hoch | CA, Kritische Systeme |
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using var ctx = PqCryptoContext.Initialize(); // Schlüssel generieren using var keyPair = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65); // Als verschlüsseltes PEM speichern (PKCS#8) keyPair.ToEncryptedPemFile( path: "private-key.pem", password: "StrongPassword123!", pbeAlgorithm: PbeAlgorithm.Aes256Cbc, prf: PbePrf.HmacSha256, iterations: 100000 // PBKDF2 Iterationen ); // Public Key (unverschlüsselt, öffentlich teilbar) keyPair.PublicKey.ToPemFile("public-key.pem"); Console.WriteLine("Schlüssel gespeichert:"); Console.WriteLine(" Private: private-key.pem (AES-256-CBC verschlüsselt)"); Console.WriteLine(" Public: public-key.pem");
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) { // Mit DPAPI verschlüsseln var protectedData = ProtectedData.Protect( privateKey, entropy: Encoding.UTF8.GetBytes(keyId), // Zusätzliche Entropie scope: scope ); var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi"); File.WriteAllBytes(keyPath, protectedData); // ACL setzen (nur aktueller Benutzer) var fileSecurity = new FileSecurity(); fileSecurity.SetAccessRuleProtection(true, false); // Vererbung deaktivieren fileSecurity.AddAccessRule(new FileSystemAccessRule( Environment.UserName, FileSystemRights.Read | FileSystemRights.Write, AccessControlType.Allow )); var fileInfo = new FileInfo(keyPath); fileInfo.SetAccessControl(fileSecurity); Console.WriteLine($"Schlüssel mit DPAPI gespeichert: {keyPath}"); } public byte[] LoadKey(string keyId, DataProtectionScope scope = DataProtectionScope.CurrentUser) { var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi"); if (!File.Exists(keyPath)) { throw new FileNotFoundException($"Schlüssel nicht gefunden: {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)) { // Sichere Löschung: Überschreiben vor Löschen 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($"Schlüssel gelöscht: {keyId}"); } } }
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) { // Key als Secret speichern (für PQ-Keys) var secretClient = new SecretClient( new Uri(_vaultUrl), new DefaultAzureCredential() ); var base64Key = Convert.ToBase64String(privateKeyPkcs8); await secretClient.SetSecretAsync(keyName, base64Key); Console.WriteLine($"Schlüssel in Key Vault gespeichert: {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) { // Neues Secret erstellen (versioniert) await StoreKeyAsync(keyName, newPrivateKey); // Alte Version bleibt als Backup verfügbar Console.WriteLine($"Schlüssel rotiert: {keyName}"); } }
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($"Schlüssel in Vault gespeichert: {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; } }
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), // Nicht exportierbar! new ObjectAttribute(CKA.CKA_LABEL, keyLabel), new ObjectAttribute(CKA.CKA_VALUE, privateKey) }; session.CreateObject(keyAttributes); Console.WriteLine($"Schlüssel im HSM gespeichert: {keyLabel}"); } finally { // Key-Daten im Speicher überschreiben Array.Clear(privateKey, 0, privateKey.Length); session.Logout(); } } }
| Branche | Speicherort | Verschlüsselung | Zugriffskontrolle |
|---|---|---|---|
| Entwicklung | Dateisystem | PKCS#8 | Dateiberechtigungen |
| Enterprise | Key Vault / Vault | AES-256 | RBAC |
| Finanzsektor | HSM (FIPS 140-3) | Hardware | Multi-Person |
| Healthcare | HSM oder Vault | AES-256 | Audit-Log |
| CA/PKI | HSM | Hardware | n-of-m Quorum |
| Beziehung | Szenario | Beschreibung |
|---|---|---|
| Voraussetzung | 11.1 Schlüsselgenerierung | Schlüssel erstellen |
| Nächster Schritt | 11.3 Schlüssel-Rotation | Regelmäßiger Austausch |
| Verwandt | 11.4 Schlüssel-Backup | Sicherungskopien |
| Verwandt | 11.5 Schlüsselvernichtung | Sichere Löschung |
« ← 11.1 Schlüsselgenerierung | ↑ Schlüssel-Übersicht | 11.3 Schlüssel-Rotation → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional