~~NOTOC~~ ====== Scenarij 11.2: Pohrana ključeva ====== **Kategorija:** [[.:start|Upravljanje ključevima]] \\ **Složenost:** ⭐⭐⭐⭐ (Visoka) \\ **Preduvjeti:** Generirani ključevi \\ **Procijenjeno vrijeme:** 20-30 minuta ---- ===== Opis ===== Ovaj scenarij opisuje **sigurnu pohranu kriptografskih ključeva**. Zaštita privatnih ključeva je esencijalna - kompromitirani ključevi ugrožavaju sve certifikate i potpise koji se na njima temelje. **Opcije pohrane:** ^ Opcija ^ Sigurnost ^ Trošak ^ Primjena ^ | **Datotečni sustav (šifrirano)** | Srednja | Niska | Razvoj, Testiranje | | **Windows DPAPI** | Dobra | Niska | Windows Server | | **Azure Key Vault** | Visoka | Srednja | Cloud | | **HashiCorp Vault** | Visoka | Srednja | On-Premise/Cloud | | **HSM** | Vrlo visoka | Visoka | CA, Kritični sustavi | ---- ===== Tijek rada ===== flowchart TD KEY[Privatni ključ] --> ENCRYPT[Šifriranje] ENCRYPT --> ACL[Kontrola pristupa] ACL --> STORE{Mjesto pohrane} STORE --> FILE[Datotečni sustav] STORE --> VAULT[Key Vault] STORE --> HSM[HSM] FILE --> BACKUP[Sigurnosna kopija] VAULT --> BACKUP HSM --> BACKUP style ENCRYPT fill:#fff3e0 style ACL fill:#e8f5e9 ---- ===== Primjer koda: Šifrirana datoteka (PEM) ===== using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using var ctx = PqCryptoContext.Initialize(); // Generiranje ključa using var keyPair = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65); // Spremanje kao šifrirani PEM (PKCS#8) keyPair.ToEncryptedPemFile( path: "private-key.pem", password: "StrongPassword123!", pbeAlgorithm: PbeAlgorithm.Aes256Cbc, prf: PbePrf.HmacSha256, iterations: 100000 // PBKDF2 iteracije ); // Javni ključ (nešifriran, može se javno dijeliti) keyPair.PublicKey.ToPemFile("public-key.pem"); Console.WriteLine("Ključevi spremljeni:"); Console.WriteLine(" Privatni: private-key.pem (AES-256-CBC šifrirano)"); Console.WriteLine(" Javni: public-key.pem"); ---- ===== Primjer koda: Windows DPAPI ===== using System.Security.Cryptography; public class DpapiKeyStorage { private readonly string _keyStorePath; public DpapiKeyStorage(string basePath = null) { _keyStorePath = basePath ?? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WvdS", "Keys" ); Directory.CreateDirectory(_keyStorePath); } public void StoreKey(string keyId, byte[] privateKey, DataProtectionScope scope = DataProtectionScope.CurrentUser) { // Šifriranje s DPAPI var protectedData = ProtectedData.Protect( privateKey, entropy: Encoding.UTF8.GetBytes(keyId), // Dodatna entropija scope: scope ); var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi"); File.WriteAllBytes(keyPath, protectedData); // Postavljanje ACL-a (samo trenutni korisnik) var fileSecurity = new FileSecurity(); fileSecurity.SetAccessRuleProtection(true, false); // Deaktivacija nasljeđivanja fileSecurity.AddAccessRule(new FileSystemAccessRule( Environment.UserName, FileSystemRights.Read | FileSystemRights.Write, AccessControlType.Allow )); var fileInfo = new FileInfo(keyPath); fileInfo.SetAccessControl(fileSecurity); Console.WriteLine($"Ključ pohranjen s DPAPI: {keyPath}"); } public byte[] LoadKey(string keyId, DataProtectionScope scope = DataProtectionScope.CurrentUser) { var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi"); if (!File.Exists(keyPath)) { throw new FileNotFoundException($"Ključ nije pronađen: {keyId}"); } var protectedData = File.ReadAllBytes(keyPath); return ProtectedData.Unprotect( protectedData, entropy: Encoding.UTF8.GetBytes(keyId), scope: scope ); } public void DeleteKey(string keyId) { var keyPath = Path.Combine(_keyStorePath, $"{keyId}.dpapi"); if (File.Exists(keyPath)) { // Sigurno brisanje: Prepisivanje prije brisanja var fileLength = new FileInfo(keyPath).Length; using (var fs = new FileStream(keyPath, FileMode.Open, FileAccess.Write)) { var zeros = new byte[fileLength]; fs.Write(zeros, 0, zeros.Length); fs.Flush(); } File.Delete(keyPath); Console.WriteLine($"Ključ izbrisan: {keyId}"); } } } ---- ===== Primjer koda: Azure Key Vault ===== using Azure.Security.KeyVault.Keys; using Azure.Security.KeyVault.Keys.Cryptography; using Azure.Identity; public class AzureKeyVaultStorage { private readonly KeyClient _keyClient; private readonly string _vaultUrl; public AzureKeyVaultStorage(string vaultUrl) { _vaultUrl = vaultUrl; _keyClient = new KeyClient( new Uri(vaultUrl), new DefaultAzureCredential() ); } public async Task StoreKeyAsync(string keyName, byte[] privateKeyPkcs8) { // Pohrana ključa kao Secret (za PQ ključeve) var secretClient = new SecretClient( new Uri(_vaultUrl), new DefaultAzureCredential() ); var base64Key = Convert.ToBase64String(privateKeyPkcs8); await secretClient.SetSecretAsync(keyName, base64Key); Console.WriteLine($"Ključ pohranjen u Key Vault: {keyName}"); return keyName; } public async Task LoadKeyAsync(string keyName) { var secretClient = new SecretClient( new Uri(_vaultUrl), new DefaultAzureCredential() ); var secret = await secretClient.GetSecretAsync(keyName); return Convert.FromBase64String(secret.Value.Value); } public async Task RotateKeyAsync(string keyName, byte[] newPrivateKey) { // Kreiranje novog Secreta (verzioniran) await StoreKeyAsync(keyName, newPrivateKey); // Stara verzija ostaje dostupna kao sigurnosna kopija Console.WriteLine($"Ključ rotiran: {keyName}"); } } ---- ===== Primjer koda: HashiCorp Vault ===== using VaultSharp; using VaultSharp.V1.AuthMethods.Token; public class HashiCorpVaultStorage { private readonly IVaultClient _vaultClient; private readonly string _mountPath; public HashiCorpVaultStorage(string vaultAddress, string token, string mountPath = "secret") { var authMethod = new TokenAuthMethodInfo(token); var settings = new VaultClientSettings(vaultAddress, authMethod); _vaultClient = new VaultClient(settings); _mountPath = mountPath; } public async Task StoreKeyAsync(string keyPath, byte[] privateKey, Dictionary metadata = null) { var secretData = new Dictionary { ["privateKey"] = Convert.ToBase64String(privateKey), ["algorithm"] = "ML-DSA-65", ["createdAt"] = DateTime.UtcNow.ToString("O") }; if (metadata != null) { foreach (var (key, value) in metadata) { secretData[key] = value; } } await _vaultClient.V1.Secrets.KeyValue.V2.WriteSecretAsync( path: keyPath, data: secretData, mountPoint: _mountPath ); Console.WriteLine($"Ključ pohranjen u Vault: {keyPath}"); } public async Task LoadKeyAsync(string keyPath) { var secret = await _vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync( path: keyPath, mountPoint: _mountPath ); var base64Key = secret.Data.Data["privateKey"].ToString(); return Convert.FromBase64String(base64Key); } public async Task> ListKeysAsync(string prefix = "") { var list = await _vaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync( path: prefix, mountPoint: _mountPath ); return list.Data.Keys; } } ---- ===== HSM integracija (PKCS#11) ===== public class HsmKeyStorage { private readonly string _pkcs11LibPath; private readonly string _tokenLabel; public HsmKeyStorage(string pkcs11LibPath, string tokenLabel) { _pkcs11LibPath = pkcs11LibPath; _tokenLabel = tokenLabel; } public void StoreKey(string keyLabel, byte[] privateKey, string userPin) { using var pkcs11Library = new Pkcs11Library(_pkcs11LibPath); var slot = pkcs11Library.GetSlotList(SlotsType.WithTokenPresent) .First(s => s.GetTokenInfo().Label.Trim() == _tokenLabel); using var session = slot.OpenSession(SessionType.ReadWrite); session.Login(CKU.CKU_USER, userPin); try { var keyAttributes = new List { new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY), new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_GENERIC_SECRET), new ObjectAttribute(CKA.CKA_TOKEN, true), new ObjectAttribute(CKA.CKA_PRIVATE, true), new ObjectAttribute(CKA.CKA_SENSITIVE, true), new ObjectAttribute(CKA.CKA_EXTRACTABLE, false), // Nije izvoziv! new ObjectAttribute(CKA.CKA_LABEL, keyLabel), new ObjectAttribute(CKA.CKA_VALUE, privateKey) }; session.CreateObject(keyAttributes); Console.WriteLine($"Ključ pohranjen u HSM: {keyLabel}"); } finally { // Prepisivanje podataka o ključu u memoriji Array.Clear(privateKey, 0, privateKey.Length); session.Logout(); } } } ---- ===== Zahtjevi za pohranu specifični za industriju ===== ^ Industrija ^ Mjesto pohrane ^ Šifriranje ^ Kontrola pristupa ^ | **Razvoj** | Datotečni sustav | PKCS#8 | Dozvole datoteka | | **Enterprise** | Key Vault / Vault | AES-256 | RBAC | | **Financijski sektor** | HSM (FIPS 140-3) | Hardversko | Višeosobna kontrola | | **Zdravstvo** | HSM ili Vault | AES-256 | Audit-Log | | **CA/PKI** | HSM | Hardversko | n-of-m Kvorum | ---- ===== Povezani scenariji ===== ^ Povezanost ^ Scenarij ^ Opis ^ | **Preduvjet** | [[.:generierung|11.1 Generiranje ključeva]] | Kreiranje ključeva | | **Sljedeći korak** | [[.:rotation|11.3 Rotacija ključeva]] | Redovita zamjena | | **Povezano** | [[.:backup|11.4 Sigurnosna kopija ključeva]] | Sigurnosne kopije | | **Povezano** | [[.:vernichtung|11.5 Uništavanje ključeva]] | Sigurno brisanje | ---- << [[.:generierung|← 11.1 Generiranje ključeva]] | [[.:start|↑ Pregled ključeva]] | [[.:rotation|11.3 Rotacija ključeva →]] >> {{tag>scenarij ključ pohrana dpapi vault hsm}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//