Get a Pentest and security assessment of your IT network.

Cyber Security

Securely Store RSA Keys in .NET

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

  1. Generate an RSA Key Pair:
    • Use the RSACryptoServiceProvider class 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);
    
  2. Serialize the Key Pair:
    • 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);
    }
    
  3. Encrypt with DPAPI:
    • 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);
    
  4. Store the Encrypted Key:
    • Save the encryptedKey byte 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.
  5. Decrypt with DPAPI:
    • 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));
    
  6. Important Considerations:
    • 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. CurrentUser protects the key only for the current user, while LocalMachine protects 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.
Related posts
Cyber Security

Zip Codes & PII: Are They Personal Data?

Cyber Security

Zero-Day Vulnerabilities: User Defence Guide

Cyber Security

Zero Knowledge Voting with Trusted Server

Cyber Security

ZeroNet: 51% Attack Risks & Mitigation