~~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//