~~NOTOC~~
====== Szenario 11.4: Schlüssel-Backup ======
**Kategorie:** [[.:start|Schlüsselmanagement]] \\
**Komplexität:** ⭐⭐⭐⭐ (Hoch) \\
**Voraussetzungen:** Generierte Schlüssel, Backup-Infrastruktur \\
**Geschätzte Zeit:** 25-35 Minuten
----
===== Beschreibung =====
Dieses Szenario beschreibt das **sichere Backup kryptographischer Schlüssel**. Ein durchdachtes Backup-Konzept ist entscheidend für Business Continuity und Disaster Recovery.
**Backup-Strategien:**
* **Verschlüsseltes Backup** - AES-256 oder PQ-hybrid
* **Key Splitting** - Shamir's Secret Sharing
* **Geografische Verteilung** - Mehrere Standorte
* **Offline-Backup** - Air-Gapped Systeme
* **HSM-Backup** - Hardware-basiert
----
===== Workflow =====
flowchart TD
KEY[Private Key] --> ENCRYPT[Verschlüsseln]
ENCRYPT --> SPLIT{Splitting?}
SPLIT -->|Ja| SHAMIR[Shamir Split n-of-m]
SPLIT -->|Nein| SINGLE[Einzelnes Backup]
SHAMIR --> DIST[Verteilen]
SINGLE --> STORE[Speichern]
DIST --> LOC1[Standort 1]
DIST --> LOC2[Standort 2]
DIST --> LOC3[Standort 3]
STORE --> OFFSITE[Offsite Storage]
LOC1 --> VERIFY[Restore-Test]
LOC2 --> VERIFY
LOC3 --> VERIFY
OFFSITE --> VERIFY
style ENCRYPT fill:#fff3e0
style VERIFY fill:#e8f5e9
----
===== Code-Beispiel: Verschlüsseltes 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. 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(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; }
}
----
===== Code-Beispiel: 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 Schema
// z.B. 3-of-5: Mindestens 3 von 5 Shares benötigt
var gf = new ExtendedEuclideanAlgorithm();
var shamir = new ShamirsSecretSharing(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 shareIndices)
{
if (shareIndices.Count < shares.Threshold)
{
throw new ArgumentException(
$"Mindestens {shares.Threshold} Shares benötigt, aber nur {shareIndices.Count} vorhanden"
);
}
var gf = new ExtendedEuclideanAlgorithm();
var shamir = new ShamirsSecretSharing(gf);
// Shares sammeln
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();
// 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 Shares { get; set; }
}
public class Share
{
public int Index { get; set; }
public string Value { get; set; }
}
----
===== Code-Beispiel: HSM-Backup =====
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
{
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(
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
{
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();
}
}
}
----
===== Backup-Strategie nach Schlüsseltyp =====
^ 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 |
----
===== Branchenspezifische Backup-Anforderungen =====
^ 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 |
----
===== Verwandte Szenarien =====
^ Beziehung ^ Szenario ^ Beschreibung ^
| **Voraussetzung** | [[.:speicherung|11.2 Schlüsselspeicherung]] | Schlüssel sicher aufbewahren |
| **Verwandt** | [[.:rotation|11.3 Schlüssel-Rotation]] | Vor Rotation sichern |
| **Nächster Schritt** | [[.:vernichtung|11.5 Schlüsselvernichtung]] | Alte Backups löschen |
| **Verwandt** | [[de:int:pqcrypt:szenarien:verwaltung:backup|4.4 Zertifikats-Backup]] | Zertifikate sichern |
----
<< [[.:rotation|← 11.3 Schlüssel-Rotation]] | [[.:start|↑ Schlüssel-Übersicht]] | [[.:vernichtung|11.5 Schlüsselvernichtung →]] >>
{{tag>szenario schluessel backup shamir disaster-recovery hsm}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//