Scenarij 11.2: Pohrana ključeva
Kategorija: 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<string> 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<byte[]> 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<string, string> metadata = null) { var secretData = new Dictionary<string, object> { ["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<byte[]> 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<IEnumerable<string>> 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<ObjectAttribute> { 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 | 11.1 Generiranje ključeva | Kreiranje ključeva |
| Sljedeći korak | 11.3 Rotacija ključeva | Redovita zamjena |
| Povezano | 11.4 Sigurnosna kopija ključeva | Sigurnosne kopije |
| Povezano | 11.5 Uništavanje ključeva | Sigurno brisanje |
« ← 11.1 Generiranje ključeva | ↑ Pregled ključeva | 11.3 Rotacija ključeva → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional
Zuletzt geändert: 30.01.2026. u 00:28