~~NOTOC~~
====== Szenario 11.2: Schlüsselspeicherung ======
**Kategorie:** [[.:start|Schlüsselmanagement]] \\
**Komplexität:** ⭐⭐⭐⭐ (Hoch) \\
**Voraussetzungen:** Generierte Schlüssel \\
**Geschätzte Zeit:** 20-30 Minuten
----
===== Beschreibung =====
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 |
----
===== Workflow =====
flowchart TD
KEY[Private Key] --> ENCRYPT[Verschlüsseln]
ENCRYPT --> ACL[Zugriffskontrolle]
ACL --> STORE{Speicherort}
STORE --> FILE[Dateisystem]
STORE --> VAULT[Key Vault]
STORE --> HSM[HSM]
FILE --> BACKUP[Backup]
VAULT --> BACKUP
HSM --> BACKUP
style ENCRYPT fill:#fff3e0
style ACL fill:#e8f5e9
----
===== Code-Beispiel: Verschlüsselte Datei (PEM) =====
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");
----
===== Code-Beispiel: 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)
{
// 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}");
}
}
}
----
===== Code-Beispiel: 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)
{
// 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 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}");
}
}
----
===== Code-Beispiel: 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($"Schlüssel in Vault gespeichert: {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), // 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();
}
}
}
----
===== Branchenspezifische Speicheranforderungen =====
^ 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 |
----
===== Verwandte Szenarien =====
^ Beziehung ^ Szenario ^ Beschreibung ^
| **Voraussetzung** | [[.:generierung|11.1 Schlüsselgenerierung]] | Schlüssel erstellen |
| **Nächster Schritt** | [[.:rotation|11.3 Schlüssel-Rotation]] | Regelmäßiger Austausch |
| **Verwandt** | [[.:backup|11.4 Schlüssel-Backup]] | Sicherungskopien |
| **Verwandt** | [[.:vernichtung|11.5 Schlüsselvernichtung]] | Sichere Löschung |
----
<< [[.:generierung|← 11.1 Schlüsselgenerierung]] | [[.:start|↑ Schlüssel-Übersicht]] | [[.:rotation|11.3 Schlüssel-Rotation →]] >>
{{tag>szenario schluessel speicherung dpapi vault hsm}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//