~~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//