Kategorie: Schlüsselmanagement
Komplexität: ⭐⭐⭐⭐ (Hoch)
Voraussetzungen: Generierte Schlüssel, Backup-Infrastruktur
Geschätzte Zeit: 25-35 Minuten
Dieses Szenario beschreibt das sichere Backup kryptographischer Schlüssel. Ein durchdachtes Backup-Konzept ist entscheidend für Business Continuity und Disaster Recovery.
Backup-Strategien:
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using System.Security.Cryptography; public class KeyBackupService { public void CreateEncryptedBackup( AsymmetricAlgorithm privateKey, string backupPath, string backupPassword, BackupOptions options = null) { options ??= BackupOptions.Default; using var ctx = PqCryptoContext.Initialize(); // 1. Private Key als PKCS#8 exportieren byte[] pkcs8 = privateKey switch { RSA rsa => rsa.ExportPkcs8PrivateKey(), ECDsa ecdsa => ecdsa.ExportPkcs8PrivateKey(), _ => ctx.ExportPrivateKey(privateKey) }; // 2. Key Encryption Key (KEK) ableiten var salt = RandomNumberGenerator.GetBytes(32); var kek = ctx.DeriveKey( backupPassword, salt, outputLength: 32, algorithm: KeyDerivationAlgorithm.Argon2id, iterations: 4, memoryKiB: 65536, parallelism: 4 ); // 3. Mit AES-256-GCM verschlüsseln var nonce = RandomNumberGenerator.GetBytes(12); var ciphertext = new byte[pkcs8.Length]; var tag = new byte[16]; using var aes = new OpenSslAesGcm(kek); aes.Encrypt(nonce, pkcs8, ciphertext, tag); // 4. Backup-Datei erstellen var backup = new KeyBackup { Version = 1, Algorithm = "ML-DSA-65", CreatedAt = DateTime.UtcNow, KdfAlgorithm = "Argon2id", KdfSalt = Convert.ToBase64String(salt), KdfIterations = 4, KdfMemoryKiB = 65536, EncryptionAlgorithm = "AES-256-GCM", Nonce = Convert.ToBase64String(nonce), Tag = Convert.ToBase64String(tag), EncryptedKey = Convert.ToBase64String(ciphertext), Checksum = ComputeChecksum(pkcs8) }; // 5. Als JSON speichern var json = JsonSerializer.Serialize(backup, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(backupPath, json); // 6. Speicher bereinigen CryptographicOperations.ZeroMemory(pkcs8); CryptographicOperations.ZeroMemory(kek); Console.WriteLine($"Backup erstellt: {backupPath}"); Console.WriteLine($" Checksum: {backup.Checksum}"); } public AsymmetricAlgorithm RestoreFromBackup( string backupPath, string backupPassword) { using var ctx = PqCryptoContext.Initialize(); // 1. Backup laden var json = File.ReadAllText(backupPath); var backup = JsonSerializer.Deserialize<KeyBackup>(json); // 2. KEK ableiten (gleiche Parameter wie beim Backup) var salt = Convert.FromBase64String(backup.KdfSalt); var kek = ctx.DeriveKey( backupPassword, salt, outputLength: 32, algorithm: KeyDerivationAlgorithm.Argon2id, iterations: backup.KdfIterations, memoryKiB: backup.KdfMemoryKiB, parallelism: 4 ); // 3. Entschlüsseln var nonce = Convert.FromBase64String(backup.Nonce); var tag = Convert.FromBase64String(backup.Tag); var ciphertext = Convert.FromBase64String(backup.EncryptedKey); var pkcs8 = new byte[ciphertext.Length]; using var aes = new OpenSslAesGcm(kek); aes.Decrypt(nonce, ciphertext, tag, pkcs8); // 4. Checksum verifizieren var checksum = ComputeChecksum(pkcs8); if (checksum != backup.Checksum) { throw new CryptographicException("Backup-Checksum ungültig"); } // 5. Key importieren var key = ctx.ImportPrivateKey(pkcs8, backup.Algorithm); CryptographicOperations.ZeroMemory(pkcs8); CryptographicOperations.ZeroMemory(kek); Console.WriteLine($"Backup wiederhergestellt: {backup.Algorithm}"); return key; } private string ComputeChecksum(byte[] data) { var hash = SHA256.HashData(data); return Convert.ToHexString(hash).Substring(0, 16); } } public class KeyBackup { public int Version { get; set; } public string Algorithm { get; set; } public DateTime CreatedAt { get; set; } public string KdfAlgorithm { get; set; } public string KdfSalt { get; set; } public int KdfIterations { get; set; } public int KdfMemoryKiB { get; set; } public string EncryptionAlgorithm { get; set; } public string Nonce { get; set; } public string Tag { get; set; } public string EncryptedKey { get; set; } public string Checksum { get; set; } }
using SecretSharingDotNet.Cryptography; public class ShamirKeyBackup { public ShamirShares SplitKey( byte[] privateKey, int totalShares, int threshold) { // Shamir's Secret Sharing: n-of-m Schema // z.B. 3-of-5: Mindestens 3 von 5 Shares benötigt var gf = new ExtendedEuclideanAlgorithm<BigInteger>(); var shamir = new ShamirsSecretSharing<BigInteger>(gf); // Key als BigInteger var secret = new BigInteger(privateKey, isUnsigned: true); // In Shares aufteilen var shares = shamir.MakeShares( (uint)threshold, (uint)totalShares, secret ); var result = new ShamirShares { Threshold = threshold, TotalShares = totalShares, Shares = shares.Select((s, i) => new Share { Index = i + 1, Value = Convert.ToBase64String(s.Y.ToByteArray()) }).ToList() }; Console.WriteLine($"Key in {totalShares} Shares aufgeteilt (Threshold: {threshold})"); return result; } public byte[] RecombineKey(ShamirShares shares, List<int> shareIndices) { if (shareIndices.Count < shares.Threshold) { throw new ArgumentException( $"Mindestens {shares.Threshold} Shares benötigt, aber nur {shareIndices.Count} vorhanden" ); } var gf = new ExtendedEuclideanAlgorithm<BigInteger>(); var shamir = new ShamirsSecretSharing<BigInteger>(gf); // Shares sammeln var selectedShares = shareIndices .Select(i => shares.Shares.First(s => s.Index == i)) .Select(s => new FinitePoint<BigInteger>( s.Index, new BigInteger(Convert.FromBase64String(s.Value), isUnsigned: true) )) .ToArray(); // Secret rekonstruieren var secret = shamir.Reconstruction(selectedShares); return secret.ToByteArray(); } public void DistributeShares(ShamirShares shares, string[] recipients) { // Shares an verschiedene Personen/Standorte verteilen for (int i = 0; i < shares.Shares.Count && i < recipients.Length; i++) { var share = shares.Shares[i]; var recipient = recipients[i]; // In der Praxis: Verschlüsselt an Empfänger senden Console.WriteLine($"Share {share.Index} → {recipient}"); // Beispiel: Als QR-Code oder USB-Stick var shareFile = $"share-{share.Index}-{recipient.Replace(" ", "_")}.json"; File.WriteAllText(shareFile, JsonSerializer.Serialize(share)); } } } public class ShamirShares { public int Threshold { get; set; } public int TotalShares { get; set; } public List<Share> Shares { get; set; } } public class Share { public int Index { get; set; } public string Value { get; set; } }
public class HsmBackupService { public void BackupHsmKey(string keyLabel, string backupPin) { // HSM-Keys sind oft nicht exportierbar! // Alternative: Key Wrapping mit Master Backup Key using var pkcs11 = new Pkcs11Library("softhsm2.dll"); var slot = pkcs11.GetSlotList(SlotsType.WithTokenPresent).First(); using var session = slot.OpenSession(SessionType.ReadWrite); session.Login(CKU.CKU_USER, backupPin); try { // 1. Zu sichernden Key finden var keyToBackup = session.FindAllObjects(new List<ObjectAttribute> { new ObjectAttribute(CKA.CKA_LABEL, keyLabel), new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY) }).FirstOrDefault(); if (keyToBackup == null) throw new Exception($"Key nicht gefunden: {keyLabel}"); // 2. Backup Wrapping Key finden/erstellen var wrappingKey = GetOrCreateWrappingKey(session); // 3. Key wrappen (verschlüsselt exportieren) var mechanism = new Mechanism(CKM.CKM_AES_KEY_WRAP); var wrappedKey = session.WrapKey(mechanism, wrappingKey, keyToBackup); // 4. Wrapped Key speichern var backup = new HsmKeyBackup { KeyLabel = keyLabel, WrappingKeyId = GetKeyId(wrappingKey), WrappedKeyData = Convert.ToBase64String(wrappedKey), Mechanism = "AES-KEY-WRAP", CreatedAt = DateTime.UtcNow }; File.WriteAllText( $"hsm-backup-{keyLabel}.json", JsonSerializer.Serialize(backup) ); Console.WriteLine($"HSM Key Backup erstellt: {keyLabel}"); } finally { session.Logout(); } } public void RestoreHsmKey(string backupFile, string userPin) { var backup = JsonSerializer.Deserialize<HsmKeyBackup>( File.ReadAllText(backupFile) ); using var pkcs11 = new Pkcs11Library("softhsm2.dll"); var slot = pkcs11.GetSlotList(SlotsType.WithTokenPresent).First(); using var session = slot.OpenSession(SessionType.ReadWrite); session.Login(CKU.CKU_USER, userPin); try { // Wrapping Key laden var wrappingKey = FindKeyById(session, backup.WrappingKeyId); // Key unwrappen var mechanism = new Mechanism(CKM.CKM_AES_KEY_WRAP); var wrappedData = Convert.FromBase64String(backup.WrappedKeyData); var keyAttributes = new List<ObjectAttribute> { new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY), new ObjectAttribute(CKA.CKA_LABEL, $"{backup.KeyLabel}-restored"), new ObjectAttribute(CKA.CKA_TOKEN, true), new ObjectAttribute(CKA.CKA_PRIVATE, true), new ObjectAttribute(CKA.CKA_SENSITIVE, true) }; var restoredKey = session.UnwrapKey(mechanism, wrappingKey, wrappedData, keyAttributes); Console.WriteLine($"HSM Key wiederhergestellt: {backup.KeyLabel}"); } finally { session.Logout(); } } }
| Schlüsseltyp | Backup-Methode | Copies | Standorte | Retention |
|---|---|---|---|---|
| Root-CA | Shamir 3-of-5 + HSM | 5 | 3 geo-verteilt | Permanent |
| Intermediate-CA | Encrypted + HSM | 3 | 2 Standorte | 10 Jahre |
| TLS Server | Encrypted | 2 | Datacenter + Offsite | 2 Jahre |
| Code Signing | Shamir 2-of-3 | 3 | 2 Standorte | 5 Jahre |
| User Keys | Encrypted | 2 | Local + Cloud | 1 Jahr |
| Branche | Anforderung | Methode | Besonderheit |
|---|---|---|---|
| Finanzsektor | PCI-DSS | HSM + Dual Control | Audit-Log Pflicht |
| Healthcare | HIPAA | Key Escrow | Zugriff für Notfälle |
| Behörden | VS-NfD | Tresor + 2-Personen | Geheimschutzbeauftragter |
| Energie | KRITIS | Offline-Backup | Air-Gapped System |
| Beziehung | Szenario | Beschreibung |
|---|---|---|
| Voraussetzung | 11.2 Schlüsselspeicherung | Schlüssel sicher aufbewahren |
| Verwandt | 11.3 Schlüssel-Rotation | Vor Rotation sichern |
| Nächster Schritt | 11.5 Schlüsselvernichtung | Alte Backups löschen |
| Verwandt | 4.4 Zertifikats-Backup | Zertifikate sichern |
« ← 11.3 Schlüssel-Rotation | ↑ Schlüssel-Übersicht | 11.5 Schlüsselvernichtung → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional