Overview
This implementation uses the Key Container feature of the CryptoAPI (CAPI) in Windows to persist RSA key pairs at the OS level. Instead of managing keys as files, you specify a “Container Name” linked to the user profile or machine store. This allows your application to retrieve the same private key even after a restart.
Specifications (Input/Output)
- Input: Key container name (a unique identifier).
- Output:
- XML string representation of the RSA key pair stored in the container.
- Permanent storage in the OS key store (side effect).
- Behavior: If a key does not exist under the specified container name, a new one is generated. If it exists, that key is loaded.
Basic Usage
You create a CspParameters object with the container name and pass it to the RSACryptoServiceProvider constructor.
using System.Security.Cryptography;
// Create parameters with a unique container name
var cspParams = new CspParameters
{
KeyContainerName = "MyUniqueAppName_KeyContainer"
};
// Passing this to the constructor automatically "generates" or "loads" the key
using var rsa = new RSACryptoServiceProvider(cspParams);
// Get XML including the private key (for backup purposes)
string xmlKey = rsa.ToXmlString(includePrivateParameters: true);
Full Code Example
The following code is designed to run in a Windows environment.
using System;
using System.Security.Cryptography;
using System.Runtime.Versioning;
[SupportedOSPlatform("windows")]
class Program
{
static void Main()
{
// 1. Define the container name
// Use a name that is unique to your application
const string ContainerName = "LocalAppIdentity_v1";
try
{
Console.WriteLine($"Accessing Key Container: {ContainerName}");
// 2. Setup CSP parameters
// This accesses the OS key store with the specified name
var parameters = new CspParameters
{
KeyContainerName = ContainerName,
// To share across the machine (requires Admin rights), use:
// Flags = CspProviderFlags.UseMachineKeyStore
};
// 3. Create instance (Load or create a new key)
// If the key exists in the container, it loads; otherwise, it generates and saves it
using var rsa = new RSACryptoServiceProvider(2048, parameters);
// 4. Verify key information
// Confirming the key size to verify successful loading
Console.WriteLine($"Key Loaded. Size: {rsa.KeySize} bits");
// 5. Export Private Key (XML format)
// Setting includePrivateParameters to true includes the secret data
string privateKeyXml = rsa.ToXmlString(includePrivateParameters: true);
Console.WriteLine("\n--- Private Key (XML Format) ---");
Console.WriteLine(privateKeyXml);
// 6. Export Public Key (XML format)
// Use false when distributing the key for encryption
string publicKeyXml = rsa.ToXmlString(includePrivateParameters: false);
Console.WriteLine("\n--- Public Key (XML Format) ---");
Console.WriteLine(publicKeyXml);
}
catch (PlatformNotSupportedException)
{
Console.WriteLine("Error: Key Containers are supported only on Windows.");
}
catch (CryptographicException ex)
{
Console.WriteLine($"Crypto Error: {ex.Message}");
}
}
}
Customization Points
- KeyContainerName: Change this to a unique string like your application name. If names conflict, you might accidentally overwrite keys used by other applications.
- CspProviderFlags: For web servers or services, it is common to use
CspProviderFlags.UseMachineKeyStore. This uses a store that is not tied to a specific user profile (requires administrator privileges). - Key Size: You can adjust the encryption strength by changing the first argument in the constructor (e.g.,
2048).
Important Notes
- Windows Only: This method depends on the Windows-specific CryptoAPI. It will not function on Linux or macOS. Consider PEM files if you need cross-platform support.
- Security: The string from
ToXmlString(true)contains sensitive private key data. Be extremely careful with logs or screen displays in a production environment. - Explicit Deletion: Keys stored in containers remain even after the OS is rebooted. If you no longer need a key, you must set
rsa.PersistKeyInCsp = falseand callrsa.Clear()to remove it manually.
Advanced Usage
Deleting a Key from the Container
Use this logic to clean up keys that are no longer required.
using System.Security.Cryptography;
// Specify the container to delete
var deleteParams = new CspParameters { KeyContainerName = "LocalAppIdentity_v1" };
using var rsaToDelete = new RSACryptoServiceProvider(deleteParams);
// Set this to false before disposing to delete the key from the OS store
rsaToDelete.PersistKeyInCsp = false;
rsaToDelete.Clear();
Console.WriteLine("Key container deleted.");
Conclusion
Using RSACryptoServiceProvider with CspParameters allows you to manage keys using the Windows key store features. This eliminates the need to manage external files, but it creates a dependency on Windows and requires a design that accounts for data persistence and potential manual cleanup.
