Overview
This implementation demonstrates how to read a PKCS#1 format PEM file starting with “—–BEGIN RSA PUBLIC KEY—–” and use that public key to encrypt data. By using the ImportRSAPublicKey method added in .NET Core 3.0, you can restore key information from Base64-decoded binary data (DER).
Specifications (Input/Output)
- Input: – RSA public key file (PKCS#1 format PEM:
publickey.pem).- String to be encrypted.
- Output: Encrypted Base64 string.
- File Format: The header must be
-----BEGIN RSA PUBLIC KEY-----(note thatBEGIN PUBLIC KEYis a different format).
Basic Usage
To use this, read the PEM string from the file, remove the headers, footers, and line breaks, and then convert it to binary.
using System.Security.Cryptography;
using System.Text;
// Read PEM string from file and remove headers/footers/line breaks to convert to binary
var pemStr = File.ReadAllText("publickey.pem");
var base64 = pemStr.Replace("-----BEGIN RSA PUBLIC KEY-----", "")
.Replace("-----END RSA PUBLIC KEY-----", "")
.Replace("\r", "").Replace("\n", "");
var keyBytes = Convert.FromBase64String(base64);
using var rsa = RSA.Create();
rsa.ImportRSAPublicKey(keyBytes, out _);
// Encryption
var data = Encoding.UTF8.GetBytes("Secret");
var encrypted = rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
Full Code Example
This is a console application designed for .NET 6 or later.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main()
{
// 1. Read the PKCS#1 format public key file
// Includes logic to create a dummy file if it doesn't exist for testing
string pemFilePath = "publickey.pem";
EnsureDummyKeyFileExists(pemFilePath);
try
{
// 2. Parse the PEM file (Header removal and Base64 decoding)
// ImportRSAPublicKey requires binary data (DER),
// so all text decorations (headers, footers, line breaks) must be removed.
string pemContent = File.ReadAllText(pemFilePath);
string publicKeyBase64 = pemContent
.Replace("-----BEGIN RSA PUBLIC KEY-----", "")
.Replace("-----END RSA PUBLIC KEY-----", "")
.Replace("\r", "") // For Windows line breaks
.Replace("\n", "") // For Linux/Mac line breaks
.Trim();
byte[] publicKeyBytes = Convert.FromBase64String(publicKeyBase64);
// 3. Instantiate RSA and import the key
using var rsa = RSA.Create();
// Import as PKCS#1 format (RSAPublicKey)
rsa.ImportRSAPublicKey(publicKeyBytes, out int bytesRead);
Console.WriteLine($"Key imported successfully. Read {bytesRead} bytes.");
// 4. Encryption process
string message = "This is a secret.";
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
// Encrypt using OAEP SHA256 padding
byte[] encryptedBytes = rsa.Encrypt(messageBytes, RSAEncryptionPadding.OaepSHA256);
// 5. Output result
string outputBase64 = Convert.ToBase64String(encryptedBytes);
Console.WriteLine("\n[Encrypted Output (Base64)]");
Console.WriteLine(outputBase64);
}
catch (FormatException)
{
Console.WriteLine("Error: Invalid Base64 format in PEM file.");
}
catch (CryptographicException ex)
{
Console.WriteLine($"Crypto Error: {ex.Message}");
Console.WriteLine("Check if the key is actually PKCS#1 (BEGIN RSA PUBLIC KEY).");
}
}
/// <summary>
/// Generates a PKCS#1 format public key file for testing purposes
/// </summary>
static void EnsureDummyKeyFileExists(string path)
{
if (!File.Exists(path))
{
using var rsa = RSA.Create(2048);
// Get PKCS#1 format byte array
byte[] keyBytes = rsa.ExportRSAPublicKey();
// Format manually into PEM
var sb = new StringBuilder();
sb.AppendLine("-----BEGIN RSA PUBLIC KEY-----");
sb.AppendLine(Convert.ToBase64String(keyBytes, Base64FormattingOptions.InsertLineBreaks));
sb.AppendLine("-----END RSA PUBLIC KEY-----");
File.WriteAllText(path, sb.ToString());
Console.WriteLine($"[Setup] Generated dummy key file: {path}");
}
}
}
Customization Points
- Simplifying PEM Parsing: If you are using .NET 5 or later, you can use the
rsa.ImportFromPem(File.ReadAllText(path))method. This handles header removal and Base64 decoding automatically, drastically shortening your code. - Identifying Key Formats: If the header is
-----BEGIN PUBLIC KEY-----(without “RSA”), it is in PKCS#8 (SubjectPublicKeyInfo) format. In that case, use theImportSubjectPublicKeyInfomethod or the previously mentionedImportFromPemmethod, which automatically detects the format.
Important Notes
- Strict Header Matching: The strings used in the
Replacemethod must match the file headers exactly. Any difference, such as the number of spaces, will cause the replacement to fail and lead to aFormatException. - Padding Modes: While older code might use
fOAEP: true(equivalent to OAEP SHA1),RSAEncryptionPadding.OaepSHA256is now recommended for better security. Ensure the settings match the decryption side.
Advanced Usage
Modern Implementation in .NET 5+ (ImportFromPem)
This is the recommended implementation that eliminates manual string manipulation.
using var rsa = RSA.Create();
string pemStr = File.ReadAllText("publickey.pem");
// Automatically detects and imports format (PKCS#1 or PKCS#8) based on headers
rsa.ImportFromPem(pemStr);
// Encryption remains the same
Conclusion
When reading an RSA public key from an external file, you must identify whether the file is in “PKCS#1 format” or “PKCS#8/SPKI format” based on the headers. While the ImportRSAPublicKey method requires Base64 decoding of the byte array, modern .NET environments allow you to skip these cumbersome steps by using ImportFromPem.
