~~NOTOC~~
====== Scenarij 11.4: Sigurnosna kopija ključeva ======
**Kategorija:** [[.:start|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(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();
var shamir = new ShamirsSecretSharing(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 shareIndices)
{
if (shareIndices.Count < shares.Threshold)
{
throw new ArgumentException(
$"Potrebno najmanje {shares.Threshold} dijelova, ali dostupno samo {shareIndices.Count}"
);
}
var gf = new ExtendedEuclideanAlgorithm();
var shamir = new ShamirsSecretSharing(gf);
// Prikupljanje dijelova
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();
// 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 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
{
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(
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
{
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** | [[.:speicherung|11.2 Pohrana ključeva]] | Sigurno čuvanje ključeva |
| **Povezano** | [[.:rotation|11.3 Rotacija ključeva]] | Kopiranje prije rotacije |
| **Sljedeći korak** | [[.:vernichtung|11.5 Uništavanje ključeva]] | Brisanje starih kopija |
| **Povezano** | [[hr:int:pqcrypt:szenarien:verwaltung:backup|4.4 Sigurnosna kopija certifikata]] | Sigurnosno kopiranje certifikata |
----
<< [[.:rotation|← 11.3 Rotacija ključeva]] | [[.:start|↑ Pregled ključeva]] | [[.:vernichtung|11.5 Uništavanje ključeva →]] >>
{{tag>scenarij ključ sigurnosna-kopija shamir disaster-recovery hsm}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//