TL;DR
The most secure way to store an RSA key pair in Windows .NET is using the Windows Data Protection API (DPAPI). This encrypts the private key with a key derived from the user’s credentials, making it inaccessible without their login. Avoid storing keys directly in configuration files or unencrypted databases.
Steps to Securely Store RSA Keys
- Generate an RSA Key Pair:
- Use the
RSACryptoServiceProviderclass to create a new key pair. Specify a key size of at least 2048 bits for strong security.
using System.Security.Cryptography;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
PrivateKey privateKey = rsa.ExportParameters(true);
PublicKey publicKey = rsa.ExportParameters(false);
- Convert the
RSAParameters(private and public keys) to a byte array using serialization. BinaryFormatter is an option, but consider alternatives like XmlSerializer for better compatibility or Protobuf for performance and security.
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
// Serialize the private key
using (FileStream stream = new FileStream("privateKey.bin", FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, privateKey);
}
- Use the
ProtectedData.Protect()method to encrypt the serialized private key byte array. Specify a scope (CurrentUser or LocalMachine) and entropy (a random salt).
using System.Security;
byte[] encryptedKey = ProtectedData.Protect(
privateKeyBytes,
"MyEncryptionSalt", // Entropy - important!
DataProtectionScope.CurrentUser);
- Save the
encryptedKeybyte array to a secure location, such as a file with restricted access permissions or an encrypted database field. Do *not* store it in plain text configuration files.
- When you need to use the private key, use
ProtectedData.Unprotect()to decrypt the byte array back into its original form.
byte[] decryptedKeyBytes = ProtectedData.Unprotect(
encryptedKey,
"MyEncryptionSalt", // Must match encryption salt
DataProtectionScope.CurrentUser);
// Deserialize the private key from the byte array
RSAParameters decryptedPrivateKey = (RSAParameters)formatter.Deserialize(new MemoryStream(decryptedKeyBytes));
- Entropy (Salt): Always use a unique and strong entropy value when encrypting with DPAPI. This prevents rainbow table attacks.
- Scope: Choose the appropriate scope (CurrentUser or LocalMachine) based on your application’s requirements.
CurrentUserprotects the key only for the current user, whileLocalMachineprotects it for all users on the machine. - Access Control: Restrict access permissions to the file or database field containing the encrypted key. Only authorized accounts should be able to read it.
- Key Rotation: Regularly rotate your RSA keys to minimize the impact of a potential compromise.
- Error Handling: Implement robust error handling to catch exceptions during encryption and decryption, especially
CryptographicException.
Alternatives (Less Secure)
- Storing in Configuration Files (Avoid): Never store RSA keys directly in configuration files, even if encrypted with a simple algorithm. These are easily accessible and vulnerable to attacks.
- Database Encryption: While encrypting the key within a database is better than plain text storage, DPAPI provides stronger protection tied to user credentials.