====== X509StoreExtensions ======
**Namespace:** ''WvdS.System.Security.Cryptography.X509Certificates''
Extensions for ''X509Store'' with post-quantum key persistence. Enables saving and restoring PQ keys in the Windows Certificate Store.
===== Overview =====
The Windows Certificate Store does not natively support post-quantum keys. These extensions store PQ keys in a separate, encrypted file storage:
%LOCALAPPDATA%\WvdS.Crypto\PqKeys\{thumbprint}.pqkey
The keys are automatically encrypted with DPAPI (Windows) or AES-256 (cross-platform).
===== Methods =====
^ Method ^ Description ^
| ''Add(certificate, mode)'' | Adds certificate and persists PQ keys |
| ''AddRange(collection, mode)'' | Adds multiple certificates |
| ''Remove(certificate, deletePqKeys)'' | Removes certificate and optionally PQ keys |
| ''RemoveRange(collection, deletePqKeys)'' | Removes multiple certificates |
| ''Find(findType, findValue, validOnly, restorePqKeys)'' | Searches certificates and restores PQ keys |
| ''GetCertificatesWithPqKeys()'' | All certificates with restored PQ keys |
===== Static Methods =====
^ Method ^ Description ^
| ''PersistPqKeys(certificate)'' | Manually persists PQ keys |
| ''RestorePqKeys(certificate)'' | Manually restores PQ keys |
| ''DeletePqKeys(certificate)'' | Deletes persisted PQ keys |
| ''HasPersistedPqKeys(certificate)'' | Checks if PQ keys exist |
| ''GetPersistedPqKeyThumbprints()'' | Lists all thumbprints |
===== Adding a Certificate =====
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
// Add certificate with PQ keys
var hybridCert = CreateHybridCertificate();
store.Add(hybridCert, CryptoMode.Hybrid);
// PQ keys are automatically persisted
Console.WriteLine($"PQ keys saved: {X509StoreExtensions.HasPersistedPqKeys(hybridCert)}");
===== Finding Certificate with PQ Keys =====
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
// Search and restore PQ keys
var certs = store.Find(
X509FindType.FindBySubjectName,
"MyCertificate",
validOnly: true,
restorePqKeys: true); // Automatically load PQ keys
foreach (var cert in certs)
{
Console.WriteLine($"Found: {cert.Subject}");
Console.WriteLine($"Mode: {cert.GetCryptoMode()}");
// PQ keys are now available
if (PqKeyStore.TryGetPublicKey(cert, out var pqKey))
{
Console.WriteLine($"PQ key: {pqKey.Length} bytes");
}
}
===== All Certificates with PQ Keys =====
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var allCerts = store.GetCertificatesWithPqKeys();
foreach (var cert in allCerts)
{
var hasPq = X509StoreExtensions.HasPersistedPqKeys(cert);
Console.WriteLine($"{cert.Subject}: PQ={hasPq}");
}
===== Removing a Certificate =====
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
var certToRemove = FindCertificate(store);
// With PQ key cleanup (recommended)
store.Remove(certToRemove, deletePqKeys: true);
// Without PQ key cleanup (keys remain)
store.Remove(certToRemove, deletePqKeys: false);
===== PqKeyStoreManager =====
Additional class for store management:
// Clean up orphaned keys (keys without associated certificate)
int deleted = PqKeyStoreManager.CleanupOrphanedKeys(
StoreName.My,
StoreLocation.CurrentUser);
Console.WriteLine($"{deleted} orphaned keys deleted");
// Backup all PQ keys
PqKeyStoreManager.BackupAllPqKeys(
"pq-keys-backup.enc",
"BackupPassword123!");
// Restore from backup
int restored = PqKeyStoreManager.RestoreFromBackup(
"pq-keys-backup.enc",
"BackupPassword123!");
Console.WriteLine($"{restored} keys restored");
===== Key Encryption =====
==== Windows (DPAPI) ====
DataProtectionScope.CurrentUser
- Only the current user can decrypt keys
- Keys are bound to user profile
==== Cross-Platform (AES-256) ====
Key = SHA256(MachineName + UserName)
IV = randomly generated
- Keys are bound to machine + user
- Less secure than DPAPI
===== Secure Deletion =====
PQ keys are securely deleted:
// Internally executed:
// 1. Overwrite file with random data
var random = RandomNumberGenerator.GetBytes(fileLength);
File.WriteAllBytes(keyPath, random);
// 2. Delete file
File.Delete(keyPath);
===== Example: Complete Workflow =====
// 1. Create hybrid certificate
CryptoConfig.EnablePostQuantum();
var cert = CreateHybridCertificate("CN=Hybrid Test");
// 2. Add to store
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadWrite);
store.Add(cert, CryptoMode.Hybrid);
}
// 3. Restart application...
// 4. Load again later
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var found = store.Find(
X509FindType.FindBySubjectName,
"Hybrid Test",
true,
restorePqKeys: true);
var loadedCert = found[0];
// PQ keys are restored!
var pqPublicKey = PqKeyStore.GetPublicKey(loadedCert);
Console.WriteLine($"PQ key loaded: {pqPublicKey.Length} bytes");
}
===== Storage Location =====
^ Operating System ^ Path ^
| Windows | ''%LOCALAPPDATA%\WvdS.Crypto\PqKeys\'' |
| Linux | ''~/.local/share/WvdS.Crypto/PqKeys/'' |
| macOS | ''~/Library/Application Support/WvdS.Crypto/PqKeys/'' |
===== Security Notes =====
* PQ keys are only accessible to the current user
* Create backup before system migration
* Run ''CleanupOrphanedKeys()'' regularly
* Cross-platform encryption is less secure than DPAPI
===== See Also =====
* [[.:x509certificate2exportextensions|X509Certificate2ExportExtensions]]
* [[.:x509certificate2extensions|X509Certificate2Extensions]]
* [[.:truststoreextensions|TrustStoreExtensions]]
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//