Scenarij 11.4: Sigurnosna kopija ključeva
Kategorija: Upravljanje ključevima
Složenost: ⭐⭐⭐⭐ (Visoka)
Preduvjeti: Generirani ključevi, infrastruktura za sigurnosne kopije
Procijenjeno vrijeme: 25-35 minuta
Opis
Ovaj scenarij opisuje sigurno kopiranje kriptografskih ključeva. Promišljen koncept sigurnosnog kopiranja je ključan za kontinuitet poslovanja i oporavak od katastrofe.
Strategije sigurnosnog kopiranja:
- Šifrirano kopiranje - AES-256 ili PQ-hibridno
- Dijeljenje ključa - Shamir's Secret Sharing
- Geografska distribucija - Više lokacija
- Offline kopiranje - Air-Gapped sustavi
- HSM kopiranje - Hardverski bazirano
Tijek rada
flowchart TD
KEY[Privatni ključ] --> ENCRYPT[Šifriranje]
ENCRYPT --> SPLIT{Dijeljenje?}
SPLIT -->|Da| SHAMIR[Shamir Split n-of-m]
SPLIT -->|Ne| SINGLE[Pojedinačna kopija]
SHAMIR --> DIST[Distribucija]
SINGLE --> STORE[Pohrana]
DIST --> LOC1[Lokacija 1]
DIST --> LOC2[Lokacija 2]
DIST --> LOC3[Lokacija 3]
STORE --> OFFSITE[Offsite Storage]
LOC1 --> VERIFY[Test oporavka]
LOC2 --> VERIFY
LOC3 --> VERIFY
OFFSITE --> VERIFY
style ENCRYPT fill:#fff3e0
style VERIFY fill:#e8f5e9
Primjer koda: Šifrirano kopiranje
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. Izvoz privatnog ključa kao PKCS#8 byte[] pkcs8 = privateKey switch { RSA rsa => rsa.ExportPkcs8PrivateKey(), ECDsa ecdsa => ecdsa.ExportPkcs8PrivateKey(), _ => ctx.ExportPrivateKey(privateKey) }; // 2. Izvođenje 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. Šifriranje s 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. Kreiranje datoteke sigurnosne kopije 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. Spremanje kao JSON var json = JsonSerializer.Serialize(backup, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(backupPath, json); // 6. Čišćenje memorije CryptographicOperations.ZeroMemory(pkcs8); CryptographicOperations.ZeroMemory(kek); Console.WriteLine($"Sigurnosna kopija kreirana: {backupPath}"); Console.WriteLine($" Checksum: {backup.Checksum}"); } public AsymmetricAlgorithm RestoreFromBackup( string backupPath, string backupPassword) { using var ctx = PqCryptoContext.Initialize(); // 1. Učitavanje sigurnosne kopije var json = File.ReadAllText(backupPath); var backup = JsonSerializer.Deserialize<KeyBackup>(json); // 2. Izvođenje KEK-a (isti parametri kao kod kopiranja) 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. Dešifriranje 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. Verifikacija checksuma var checksum = ComputeChecksum(pkcs8); if (checksum != backup.Checksum) { throw new CryptographicException("Checksum sigurnosne kopije nije validan"); } // 5. Uvoz ključa var key = ctx.ImportPrivateKey(pkcs8, backup.Algorithm); CryptographicOperations.ZeroMemory(pkcs8); CryptographicOperations.ZeroMemory(kek); Console.WriteLine($"Sigurnosna kopija obnovljena: {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; } }
Primjer koda: 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 shema // npr. 3-of-5: Potrebno najmanje 3 od 5 dijelova var gf = new ExtendedEuclideanAlgorithm<BigInteger>(); var shamir = new ShamirsSecretSharing<BigInteger>(gf); // Ključ kao BigInteger var secret = new BigInteger(privateKey, isUnsigned: true); // Dijeljenje na dijelove 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($"Ključ podijeljen na {totalShares} dijelova (Threshold: {threshold})"); return result; } public byte[] RecombineKey(ShamirShares shares, List<int> shareIndices) { if (shareIndices.Count < shares.Threshold) { throw new ArgumentException( $"Potrebno najmanje {shares.Threshold} dijelova, ali dostupno samo {shareIndices.Count}" ); } var gf = new ExtendedEuclideanAlgorithm<BigInteger>(); var shamir = new ShamirsSecretSharing<BigInteger>(gf); // Prikupljanje dijelova 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(); // Rekonstrukcija tajne var secret = shamir.Reconstruction(selectedShares); return secret.ToByteArray(); } public void DistributeShares(ShamirShares shares, string[] recipients) { // Distribucija dijelova različitim osobama/lokacijama for (int i = 0; i < shares.Shares.Count && i < recipients.Length; i++) { var share = shares.Shares[i]; var recipient = recipients[i]; // U praksi: Šifrirano slanje primatelju Console.WriteLine($"Dio {share.Index} → {recipient}"); // Primjer: Kao QR kod ili 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; } }
Primjer koda: HSM sigurnosna kopija
public class HsmBackupService { public void BackupHsmKey(string keyLabel, string backupPin) { // HSM ključevi često nisu izvozivi! // Alternativa: Key Wrapping s 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. Pronalazak ključa za kopiranje 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($"Ključ nije pronađen: {keyLabel}"); // 2. Pronalazak/kreiranje Backup Wrapping Key var wrappingKey = GetOrCreateWrappingKey(session); // 3. Omatanje ključa (šifrirani izvoz) var mechanism = new Mechanism(CKM.CKM_AES_KEY_WRAP); var wrappedKey = session.WrapKey(mechanism, wrappingKey, keyToBackup); // 4. Spremanje omotanog ključa 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 sigurnosna kopija kreirana: {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 { // Učitavanje Wrapping Key var wrappingKey = FindKeyById(session, backup.WrappingKeyId); // Odmatanje ključa 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 obnovljen: {backup.KeyLabel}"); } finally { session.Logout(); } } }
Strategija sigurnosnog kopiranja prema tipu ključa
| Tip ključa | Metoda kopiranja | Kopije | Lokacije | Zadržavanje |
|---|---|---|---|---|
| Root-CA | Shamir 3-of-5 + HSM | 5 | 3 geo-distribuirane | Trajno |
| Intermediate-CA | Šifrirano + HSM | 3 | 2 lokacije | 10 godina |
| TLS Server | Šifrirano | 2 | Datacenter + Offsite | 2 godine |
| Code Signing | Shamir 2-of-3 | 3 | 2 lokacije | 5 godina |
| Korisnički ključevi | Šifrirano | 2 | Lokalno + Cloud | 1 godina |
Zahtjevi za sigurnosno kopiranje specifični za industriju
| Industrija | Zahtjev | Metoda | Posebnost |
|---|---|---|---|
| Financijski sektor | PCI-DSS | HSM + Dual Control | Obvezni Audit-Log |
| Zdravstvo | HIPAA | Key Escrow | Pristup za hitne slučajeve |
| Vlada | VS-NfD | Trezor + 2 osobe | Povjerenik za sigurnost |
| Energetika | KRITIS | Offline kopiranje | Air-Gapped sustav |
Povezani scenariji
| Povezanost | Scenarij | Opis |
|---|---|---|
| Preduvjet | 11.2 Pohrana ključeva | Sigurno čuvanje ključeva |
| Povezano | 11.3 Rotacija ključeva | Kopiranje prije rotacije |
| Sljedeći korak | 11.5 Uništavanje ključeva | Brisanje starih kopija |
| Povezano | 4.4 Sigurnosna kopija certifikata | Sigurnosno kopiranje certifikata |
« ← 11.3 Rotacija ključeva | ↑ Pregled ključeva | 11.5 Uništavanje ključeva → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional
Zuletzt geändert: 30.01.2026. u 00:30