RSA
PEM
.NET
private key
cryptography

How to read a PEM RSA private key from .NET

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In .NET 5+ and .NET Core 3.1+, use RSA.Create() with ImportFromPem() to read a PEM RSA private key directly — no third-party libraries needed. For older .NET Framework, use BouncyCastle or manually strip the PEM headers and decode the Base64 content. The PEM format wraps a Base64-encoded key between -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- headers. The key may be in PKCS#1 (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) format, and the import method differs for each.

csharp
1using System.Security.Cryptography;
2
3// Read PEM file
4string pemText = File.ReadAllText("private_key.pem");
5
6// Import directly from PEM
7var rsa = RSA.Create();
8rsa.ImportFromPem(pemText.AsSpan());
9
10// Use the key
11byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, World!");
12byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
13Console.WriteLine($"Signature length: {signature.Length} bytes");

ImportFromPem() automatically detects whether the key is PKCS#1 or PKCS#8 format.

PKCS#1 vs PKCS#8 Format

 
1# PKCS#1 format (RSA-specific)
2-----BEGIN RSA PRIVATE KEY-----
3MIIEowIBAAKCAQEA...
4-----END RSA PRIVATE KEY-----
5
6# PKCS#8 format (generic, may contain RSA, EC, etc.)
7-----BEGIN PRIVATE KEY-----
8MIIEvQIBADANBgkq...
9-----END PRIVATE KEY-----
10
11# PKCS#8 encrypted
12-----BEGIN ENCRYPTED PRIVATE KEY-----
13MIIFHDBOBgkqhkiG...
14-----END ENCRYPTED PRIVATE KEY-----

Import methods for each format:

csharp
1var rsa = RSA.Create();
2
3// PKCS#1: BEGIN RSA PRIVATE KEY
4rsa.ImportRSAPrivateKey(pkcs1Bytes, out _);
5
6// PKCS#8: BEGIN PRIVATE KEY
7rsa.ImportPkcs8PrivateKey(pkcs8Bytes, out _);
8
9// PKCS#8 encrypted: BEGIN ENCRYPTED PRIVATE KEY
10rsa.ImportEncryptedPkcs8PrivateKey("password", encryptedBytes, out _);
11
12// Or let ImportFromPem detect automatically (.NET 5+)
13rsa.ImportFromPem(pemText);

Manual PEM Parsing (Older .NET)

For .NET Framework 4.x where ImportFromPem is not available:

csharp
1using System;
2using System.IO;
3using System.Security.Cryptography;
4
5public static RSA ReadPemPrivateKey(string pemFilePath)
6{
7    string pemText = File.ReadAllText(pemFilePath);
8
9    // Strip PEM headers and whitespace
10    string base64;
11    if (pemText.Contains("BEGIN RSA PRIVATE KEY"))
12    {
13        base64 = pemText
14            .Replace("-----BEGIN RSA PRIVATE KEY-----", "")
15            .Replace("-----END RSA PRIVATE KEY-----", "")
16            .Replace("\n", "").Replace("\r", "").Trim();
17
18        byte[] keyBytes = Convert.FromBase64String(base64);
19        var rsa = RSA.Create();
20        rsa.ImportRSAPrivateKey(keyBytes, out _);  // .NET Core 3.0+
21        return rsa;
22    }
23    else if (pemText.Contains("BEGIN PRIVATE KEY"))
24    {
25        base64 = pemText
26            .Replace("-----BEGIN PRIVATE KEY-----", "")
27            .Replace("-----END PRIVATE KEY-----", "")
28            .Replace("\n", "").Replace("\r", "").Trim();
29
30        byte[] keyBytes = Convert.FromBase64String(base64);
31        var rsa = RSA.Create();
32        rsa.ImportPkcs8PrivateKey(keyBytes, out _);  // .NET Core 3.0+
33        return rsa;
34    }
35
36    throw new CryptographicException("Unsupported PEM format");
37}

Using BouncyCastle (.NET Framework)

For .NET Framework 4.x without the modern import methods:

csharp
1// Install: Install-Package BouncyCastle.Cryptography
2using Org.BouncyCastle.Crypto;
3using Org.BouncyCastle.Crypto.Parameters;
4using Org.BouncyCastle.OpenSsl;
5using Org.BouncyCastle.Security;
6using System.IO;
7using System.Security.Cryptography;
8
9public static RSA ReadPemWithBouncyCastle(string pemFilePath)
10{
11    using var reader = new StreamReader(pemFilePath);
12    var pemReader = new PemReader(reader);
13    var keyObject = pemReader.ReadObject();
14
15    RsaPrivateCrtKeyParameters privateKey;
16    if (keyObject is AsymmetricCipherKeyPair keyPair)
17    {
18        privateKey = (RsaPrivateCrtKeyParameters)keyPair.Private;
19    }
20    else
21    {
22        privateKey = (RsaPrivateCrtKeyParameters)keyObject;
23    }
24
25    var rsaParams = DotNetUtilities.ToRSAParameters(privateKey);
26    var rsa = RSA.Create();
27    rsa.ImportParameters(rsaParams);
28    return rsa;
29}
30
31// With password-protected PEM
32public static RSA ReadEncryptedPem(string pemFilePath, string password)
33{
34    using var reader = new StreamReader(pemFilePath);
35    var pemReader = new PemReader(reader, new PasswordFinder(password));
36    var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
37    var privateKey = (RsaPrivateCrtKeyParameters)keyPair.Private;
38
39    var rsaParams = DotNetUtilities.ToRSAParameters(privateKey);
40    var rsa = RSA.Create();
41    rsa.ImportParameters(rsaParams);
42    return rsa;
43}
44
45class PasswordFinder : IPasswordFinder
46{
47    private readonly string _password;
48    public PasswordFinder(string password) => _password = password;
49    public char[] GetPassword() => _password.ToCharArray();
50}

Practical Examples

Signing and Verifying

csharp
1// Sign data
2var rsa = RSA.Create();
3rsa.ImportFromPem(File.ReadAllText("private.pem"));
4
5byte[] data = Encoding.UTF8.GetBytes("Important message");
6byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
7
8// Verify with public key
9var rsaPublic = RSA.Create();
10rsaPublic.ImportFromPem(File.ReadAllText("public.pem"));
11bool isValid = rsaPublic.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

Creating JWT Tokens

csharp
1using System.IdentityModel.Tokens.Jwt;
2using Microsoft.IdentityModel.Tokens;
3
4var rsa = RSA.Create();
5rsa.ImportFromPem(File.ReadAllText("private.pem"));
6
7var signingCredentials = new SigningCredentials(
8    new RsaSecurityKey(rsa),
9    SecurityAlgorithms.RsaSha256
10);
11
12var token = new JwtSecurityToken(
13    issuer: "myapp",
14    audience: "api",
15    expires: DateTime.UtcNow.AddHours(1),
16    signingCredentials: signingCredentials
17);
18
19string jwt = new JwtSecurityTokenHandler().WriteToken(token);

Common Pitfalls

  • Confusing PKCS#1 and PKCS#8 formats: ImportRSAPrivateKey expects PKCS#1 bytes (BEGIN RSA PRIVATE KEY), while ImportPkcs8PrivateKey expects PKCS#8 bytes (BEGIN PRIVATE KEY). Using the wrong method throws CryptographicException. Use ImportFromPem() (.NET 5+) to detect the format automatically.
  • Not stripping PEM headers before Base64 decoding: The -----BEGIN...----- and -----END...----- lines are not valid Base64. Passing the raw PEM string to Convert.FromBase64String() throws FormatException. Strip headers, footers, and all whitespace before decoding.
  • RSA object disposal and key lifetime: RSA.Create() returns a disposable object. In production code, dispose it with using when done. For long-lived keys (e.g., JWT signing in a web app), store the RSA instance as a singleton rather than re-reading the PEM file on every request.
  • File permissions on the PEM key: The private key file must be readable only by the application process. On Linux, set chmod 600 private.pem. On Windows, restrict NTFS permissions. Leaving a private key world-readable is a security vulnerability.
  • Using RSACryptoServiceProvider instead of RSA.Create(): The legacy RSACryptoServiceProvider does not support ImportFromPem or ImportPkcs8PrivateKey. Always use RSA.Create() which returns the modern RSACng (Windows) or RSAOpenSsl (Linux/macOS) implementation.

Summary

  • Use RSA.Create() + ImportFromPem() in .NET 5+ for the simplest approach — it auto-detects PKCS#1 and PKCS#8
  • For .NET Core 3.x, manually strip PEM headers and use ImportRSAPrivateKey() or ImportPkcs8PrivateKey()
  • For .NET Framework 4.x, use BouncyCastle's PemReader to parse the key and convert to RSAParameters
  • Always dispose RSA objects or store as singletons for long-lived signing operations
  • Protect private key files with appropriate filesystem permissions (600 on Linux, restricted NTFS on Windows)

Course illustration
Course illustration

All Rights Reserved.