Overview
This implementation reads a PKCS#1 format PEM file starting with -----BEGIN RSA PRIVATE KEY----- and uses the private key to decrypt encrypted data. By using the ImportRSAPrivateKey method added in .NET Core 3.0, you can restore key information from binary data extracted through text processing.
Specifications (Input/Output)
- Input: RSA private key file (PKCS#1 format PEM:
private.key), encrypted data (Base64 string). - Output: Decrypted plaintext (UTF-8).
- Prerequisite: The file must be in PKCS#1 format (the header must include “RSA”).
Basic Usage
To use this method, you must remove the headers and footers from the PEM string, convert it to binary, and then import it into the RSA instance.
using System.Security.Cryptography;
using System.Text;
// Convert PEM string to binary by removing headers and footers
var pemStr = File.ReadAllText("private.key");
var base64 = pemStr.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
.Replace("-----END RSA PRIVATE KEY-----", "")
.Replace("\r", "").Replace("\n", "");
var keyBytes = Convert.FromBase64String(base64);
// Initialize RSA and import
using var rsa = RSA.Create();
rsa.ImportRSAPrivateKey(keyBytes, out _);
// Decrypt (using the same padding as encryption)
var encrypted = Convert.FromBase64String("Base64Data...");
var decrypted = rsa.Decrypt(encrypted, RSAEncryptionPadding.OaepSHA256);
Full Code Example
The following code is a standalone console application that demonstrates the entire process from reading the file to decrypting the data.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main()
{
// 1. Prepare test data
// In a real scenario, use an actual encrypted Base64 string
string encryptedBase64 = "(Encrypted Base64 string goes here)";
// Setup a dummy environment for demonstration (Not needed in production)
CreateDummyEnvironment(out string keyFilePath, out encryptedBase64);
try
{
Console.WriteLine($"Reading Private Key from: {keyFilePath}");
// 2. Read and format the PEM file
// Remove PKCS#1 headers, footers, and line breaks to extract the Base64 part
string pemContent = File.ReadAllText(keyFilePath);
string privateKeyBase64 = pemContent
.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
.Replace("-----END RSA PRIVATE KEY-----", "")
.Replace("\r", "")
.Replace("\n", "")
.Trim();
byte[] privateKeyBytes = Convert.FromBase64String(privateKeyBase64);
// 3. Import private key into RSA instance
using var rsa = RSA.Create();
// Import as PKCS#1 format
rsa.ImportRSAPrivateKey(privateKeyBytes, out int bytesRead);
Console.WriteLine($"Key imported. ({bytesRead} bytes read)");
// 4. Decryption process
byte[] encryptedData = Convert.FromBase64String(encryptedBase64);
// Specify the padding mode matching the encryption (Recommended: OAEP SHA256)
byte[] decryptedData = rsa.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);
// 5. Output the result
string plainText = Encoding.UTF8.GetString(decryptedData);
Console.WriteLine("\n[Decrypted Message]");
Console.WriteLine(plainText);
}
catch (FormatException)
{
Console.WriteLine("Error: The key file format is invalid.");
}
catch (CryptographicException ex)
{
Console.WriteLine($"Decryption Error: {ex.Message}");
Console.WriteLine("Hint: Check padding mode or if the key matches the encryption key.");
}
}
// Helper to create a dummy key file and encrypted data for testing
static void CreateDummyEnvironment(out string path, out string encryptedBase64)
{
path = "test_private_pkcs1.pem";
using var rsa = RSA.Create(2048);
// Create private key file
byte[] keyBytes = rsa.ExportRSAPrivateKey();
var sb = new StringBuilder();
sb.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
sb.AppendLine(Convert.ToBase64String(keyBytes, Base64FormattingOptions.InsertLineBreaks));
sb.AppendLine("-----END RSA PRIVATE KEY-----");
File.WriteAllText(path, sb.ToString());
// Create encrypted data
byte[] data = Encoding.UTF8.GetBytes("Secret Code: 777");
byte[] enc = rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
encryptedBase64 = Convert.ToBase64String(enc);
}
}
Customization Points
- Changing Padding: Older implementations using
fOAEP: trueoften refer to OAEP with SHA1. If compatibility is required, useRSAEncryptionPadding.OaepSHA1. For new development,OaepSHA256is the standard. - Key File Format: If the file content begins with
-----BEGIN PRIVATE KEY-----(without “RSA”), it is in PKCS#8 format. In that case, use theImportPkcs8PrivateKeymethod instead.
Important Notes
- Private Key Management: The private key file is highly sensitive. You must set strict file system permissions (ACLs) to ensure only the application user can read it.
- Header Accuracy: When performing manual string replacement, even a single character error in the header string will result in an invalid Base64 string and throw an exception.
- Encrypted PEM Files: This method cannot read PEM files protected by a password (Encrypted Private Keys). Those require specific methods like
ImportEncryptedPkcs8PrivateKey.
Advanced Usage
Simplification with ImportFromPem (.NET 5+)
This is the recommended pattern in .NET 5 or later, as it eliminates manual string manipulation and automatically detects the format.
using var rsa = RSA.Create();
// Pass the file content directly; headers and formats (PKCS#1/PKCS#8) are handled automatically
string pemContent = File.ReadAllText("private.key");
rsa.ImportFromPem(pemContent);
// Decryption remains the same
var result = rsa.Decrypt(data, RSAEncryptionPadding.OaepSHA256);
Conclusion
To use a PKCS#1 format private key, use the ImportRSAPrivateKey method. This allows you to handle standard key files generated by tools like OpenSSL without converting them to XML. However, for modern .NET 5+ environments, using ImportFromPem is the most efficient and safest way to handle header processing and format identification automatically.
