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