~~NOTOC~~ ====== Scenario 11.4: Key Backup ====== **Category:** [[.:start|Key Management]] \\ **Complexity:** ⭐⭐⭐⭐ (High) \\ **Prerequisites:** Generated keys, backup infrastructure \\ **Estimated Time:** 25-35 Minutes ---- ===== Description ===== This scenario describes the **secure backup of cryptographic keys**. A well-designed backup concept is essential for business continuity and disaster recovery. **Backup Strategies:** * **Encrypted Backup** - AES-256 or PQ-hybrid * **Key Splitting** - Shamir's Secret Sharing * **Geographic Distribution** - Multiple locations * **Offline Backup** - Air-gapped systems * **HSM Backup** - Hardware-based ---- ===== Workflow ===== flowchart TD KEY[Private Key] --> ENCRYPT[Encrypt] ENCRYPT --> SPLIT{Splitting?} SPLIT -->|Yes| SHAMIR[Shamir Split n-of-m] SPLIT -->|No| SINGLE[Single Backup] SHAMIR --> DIST[Distribute] SINGLE --> STORE[Store] DIST --> LOC1[Location 1] DIST --> LOC2[Location 2] DIST --> LOC3[Location 3] STORE --> OFFSITE[Offsite Storage] LOC1 --> VERIFY[Restore Test] LOC2 --> VERIFY LOC3 --> VERIFY OFFSITE --> VERIFY style ENCRYPT fill:#fff3e0 style VERIFY fill:#e8f5e9 ---- ===== Code Example: Encrypted Backup ===== 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. Export private key as PKCS#8 byte[] pkcs8 = privateKey switch { RSA rsa => rsa.ExportPkcs8PrivateKey(), ECDsa ecdsa => ecdsa.ExportPkcs8PrivateKey(), _ => ctx.ExportPrivateKey(privateKey) }; // 2. Derive Key Encryption Key (KEK) var salt = RandomNumberGenerator.GetBytes(32); var kek = ctx.DeriveKey( backupPassword, salt, outputLength: 32, algorithm: KeyDerivationAlgorithm.Argon2id, iterations: 4, memoryKiB: 65536, parallelism: 4 ); // 3. Encrypt with AES-256-GCM 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. Create backup file 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. Save as JSON var json = JsonSerializer.Serialize(backup, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(backupPath, json); // 6. Clear memory CryptographicOperations.ZeroMemory(pkcs8); CryptographicOperations.ZeroMemory(kek); Console.WriteLine($"Backup created: {backupPath}"); Console.WriteLine($" Checksum: {backup.Checksum}"); } public AsymmetricAlgorithm RestoreFromBackup( string backupPath, string backupPassword) { using var ctx = PqCryptoContext.Initialize(); // 1. Load backup var json = File.ReadAllText(backupPath); var backup = JsonSerializer.Deserialize(json); // 2. Derive KEK (same parameters as 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. Decrypt 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. Verify checksum var checksum = ComputeChecksum(pkcs8); if (checksum != backup.Checksum) { throw new CryptographicException("Backup checksum invalid"); } // 5. Import key var key = ctx.ImportPrivateKey(pkcs8, backup.Algorithm); CryptographicOperations.ZeroMemory(pkcs8); CryptographicOperations.ZeroMemory(kek); Console.WriteLine($"Backup restored: {backup.Algorithm}"); return key; } private string ComputeChecksum(byte[] data) { var hash = SHA256.HashData(data); return Convert.ToHexString(hash).Substring(0, 16); } } ---- ===== Code Example: Shamir Secret Sharing ===== using SecretSharingDotNet.Cryptography; public class ShamirKeyBackup { public ShamirShares SplitKey( byte[] privateKey, int totalShares, int threshold) { // Shamir's Secret Sharing: n-of-m scheme // e.g., 3-of-5: At least 3 of 5 shares needed var gf = new ExtendedEuclideanAlgorithm(); var shamir = new ShamirsSecretSharing(gf); // Key as BigInteger var secret = new BigInteger(privateKey, isUnsigned: true); // Split into shares 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 split into {totalShares} shares (Threshold: {threshold})"); return result; } public byte[] RecombineKey(ShamirShares shares, List shareIndices) { if (shareIndices.Count < shares.Threshold) { throw new ArgumentException( $"At least {shares.Threshold} shares needed, but only {shareIndices.Count} provided" ); } var gf = new ExtendedEuclideanAlgorithm(); var shamir = new ShamirsSecretSharing(gf); // Collect shares var selectedShares = shareIndices .Select(i => shares.Shares.First(s => s.Index == i)) .Select(s => new FinitePoint( s.Index, new BigInteger(Convert.FromBase64String(s.Value), isUnsigned: true) )) .ToArray(); // Reconstruct secret var secret = shamir.Reconstruction(selectedShares); return secret.ToByteArray(); } public void DistributeShares(ShamirShares shares, string[] recipients) { // Distribute shares to different persons/locations for (int i = 0; i < shares.Shares.Count && i < recipients.Length; i++) { var share = shares.Shares[i]; var recipient = recipients[i]; // In practice: Send encrypted to recipient Console.WriteLine($"Share {share.Index} → {recipient}"); // Example: As QR code or USB stick var shareFile = $"share-{share.Index}-{recipient.Replace(" ", "_")}.json"; File.WriteAllText(shareFile, JsonSerializer.Serialize(share)); } } } ---- ===== Backup Strategy by Key Type ===== ^ Key Type ^ Backup Method ^ Copies ^ Locations ^ Retention ^ | **Root CA** | Shamir 3-of-5 + HSM | 5 | 3 geo-distributed | Permanent | | **Intermediate CA** | Encrypted + HSM | 3 | 2 locations | 10 years | | **TLS Server** | Encrypted | 2 | Datacenter + Offsite | 2 years | | **Code Signing** | Shamir 2-of-3 | 3 | 2 locations | 5 years | | **User Keys** | Encrypted | 2 | Local + Cloud | 1 year | ---- ===== Industry-Specific Backup Requirements ===== ^ Industry ^ Requirement ^ Method ^ Specifics ^ | **Financial Sector** | PCI-DSS | HSM + Dual Control | Audit log mandatory | | **Healthcare** | HIPAA | Key Escrow | Access for emergencies | | **Government** | VS-NfD | Safe + 2-Person | Security Officer | | **Energy** | KRITIS | Offline Backup | Air-gapped system | ---- ===== Related Scenarios ===== ^ Relationship ^ Scenario ^ Description ^ | **Prerequisite** | [[.:speicherung|11.2 Key Storage]] | Store keys securely | | **Related** | [[.:rotation|11.3 Key Rotation]] | Backup before rotation | | **Next Step** | [[.:vernichtung|11.5 Key Destruction]] | Delete old backups | | **Related** | [[en:int:pqcrypt:szenarien:verwaltung:backup|4.4 Certificate Backup]] | Backup certificates | ---- << [[.:rotation|← 11.3 Key Rotation]] | [[.:start|↑ Key Overview]] | [[.:vernichtung|11.5 Key Destruction →]] >> {{tag>scenario key backup shamir disaster-recovery hsm}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//