Skip to content

[API Proposal]: Enhance X509Store Import to Support Secure Private Key Storage (Linux & Cross-Platform) #113641

Open
@billnice250

Description

@billnice250

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 X509KeyStorageFlags that enforces secure storage of the private key during the import process.
  • The X509Store API 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

  1. Provide a flag like X509KeyStorageFlags.SecureImport that encrypts private keys automatically during the import process.
  2. Integrate with OS-native key management systems such as libsecret, GPG, or OpenSSL to securely store private keys.
  3. Allow .NET users to load and store encrypted PFX files without extracting keys to unprotected disk storage.
  4. 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

  1. 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();
  1. Use OS Secure Storage
store.Add(cert, X509KeyProtectionMode.SecureStore);
  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.Securityneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions