Szenario 11.2: Schlüsselspeicherung

Kategorie: 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<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}");
    }
}

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<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;
    }
}

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),  // 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 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