[API Proposal]: Enhance X509Store Import to Support Secure Private Key Storage (Linux & Cross-Platform)
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Feature
- Clarity
- Needs clarification
- Activity status
- Stale
- Tech stack
- csharp
- Domain
- api, cryptography, operating-systems, security
Research direction
Start with the X509Store.Import/Add entry points and X509KeyStorageFlags usage, then compare the Linux clear-text behavior described with the Windows and macOS storage models. Done requires an agreed cross-platform protection design, API shape, and tests covering secure private-key storage, but the issue identifies no files or tests.
Written by the indexing model from the issue text.
Description
Background and motivation
Problem
When importing a PFX file using X509Store.Import() or X509Certificate2.Import() on Linux, the private key is extracted and stored in clear text under:
📂 ~/.dotnet/corefx/cryptography/x509stores/
This is different from Windows, where private keys can be securely stored in KeyStore and protected by mechanisms like DPAPI.
The current behavior on Linux does not allow for encrypted storage of private keys, which poses a security risk for users.
Repro Steps
var cert = new X509Certificate2("mycert.pfx", "password",
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet);
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
🔹 This process stores the private key in clear text within ~/.dotnet/corefx/cryptography/x509stores/.
Expected Behavior
- The
X509Store.Import()functionality should allow for encrypted storage of private keys. - Alternatively, there should be a flag or mechanism in
X509KeyStorageFlagsthat enforces secure storage of the private key during the import process. - The
X509StoreAPI should handle private key storage in a more secure way, similar to how it's handled on Windows or macOS (e.g., using Keychain on macOS or TPM on Windows).
Actual Behavior
- Private keys are stored in clear text on Linux systems.
- There is no mechanism to encrypt the private key during the import process, leaving sensitive information exposed.
Proposed Solution
- Provide a flag like
X509KeyStorageFlags.SecureImportthat encrypts private keys automatically during the import process. - Integrate with OS-native key management systems such as libsecret, GPG, or OpenSSL to securely store private keys.
- Allow .NET users to load and store encrypted PFX files without extracting keys to unprotected disk storage.
- Ensure the behavior is consistent across platforms (Linux, macOS, Windows) for private key storage.
Environment
- .NET Version: 8.0 (and prior)
- OS: Linux (Ubuntu, Debian, RHEL, etc.)
- Reproducible: ✅ Yes, always
Security Risk
This exposes private keys to unauthorized access unless users manually encrypt or use external security mechanisms, which can be cumbersome and error-prone.
Additional Context
- Windows uses DPAPI for private key protection.
- macOS uses the Keychain for secure private key storage.
- Linux lacks native, consistent private key protection mechanisms for .NET applications, and users must resort to custom workarounds like encrypting the PFX file manually before import.
API Proposal
public enum X509KeyProtectionMode
{
None, // Default (current behavior, unprotected storage)
UserSecret, // Encrypts private keys using a user-provided secret
SecureStore // Uses OS-native secure storage (DPAPI, Keychain, libsecret, etc.)
}
public sealed class X509Store
{
// New overload of the Add method with key protection mode
public void Add(X509Certificate2 certificate, X509KeyProtectionMode protectionMode, string? userSecret = null)
{
if (protectionMode == X509KeyProtectionMode.UserSecret && string.IsNullOrEmpty(userSecret))
{
throw new ArgumentException("UserSecret cannot be null when using UserSecret mode.");
}
// Logic to securely store the private key using the chosen protection mode
}
// Existing method (for backward compatibility)
public void Add(X509Certificate2 certificate) { /* Default behavior */ }
}
API Usage
- Use UserSecret for Encryption
var cert = new X509Certificate2("mycert.pfx", "password",
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet);
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(cert, X509KeyProtectionMode.UserSecret, "MySecureUserSecret");
store.Close();
- Use OS Secure Storage
store.Add(cert, X509KeyProtectionMode.SecureStore);
- Default (No Protection, Current Behavior)
store.Add(cert, X509KeyProtectionMode.SecureStore);
Alternative Designs
No response
Risks
This exposes private keys to unauthorized access unless users manually encrypt or use external security mechanisms, which can be cumbersome and error-prone.
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·