System.Security.Cryptography.RSA
has been the focus of many performance and usability improvements in recent major releases of .NET (and previously .NET Core).
It’s a cross-platform, performant implementation of RSA signing and encryption that uses the operating system’s cryptography libraries rather than a software implementation such as Bouncy Castle.
In this article, you’ll learn the various ways of creating, parsing, and loading RSA keys in .NET, whether from a JSON Web Key (JWK) or a certificate generated by OpenSSL. This article serves as a reference for loading in RSA keys for my various C# articles on JWTs and cryptography. Code samples work for .NET 6 onwards.
.NET’s RSA
object
The RSA
object lives in System.Security.Cryptography
namespace.
It inherits AsymmetricAlgorithm
and uses a similar pattern to other implementations such as ECDSA
.
Your primary usage of the RSA object will likely be the SignData
and VerifyData
methods, which get called if you pass an RSA object to a SecurityKey
implementation or SignedXml
.
You can also use RSA for asymmetric encryption using the Encrypt
and Decrypt
methods.
Just don’t forget only to use OAEP as your padding scheme for encryption.
RSA
implements IDisposable
and can call into unmanaged code, so make sure you use a using
statement.
Creating a new in-memory RSA key in .NET
The simplest way to get a new RSA key is to have .NET generate one for you by calling the object’s Create
factory method.
using var key = RSA.Create(keySizeInBits: 3072);
This method has a few overloads, but I recommend passing in either the desired key length, like the example above, or an instance of valid RsaParameters
like you’ll see shortly.
While 2048 is the minimum key length required by specifications such as JOSE (JWTs), it is recommended that you use 3072, which gives you 128-bit security.
This approach is useful when writing tests or if you want your system to automatically generate and distribute keys. For example, it might generate them in memory and then store the key parameters in the database as a JSON Web Key (JWK). The public key can then be distributed to other parties so that they can validate signatures or encrypt data that only your system can read.
Loading RSA keys from a JSON Web Key (JWK) in .NET
When using distributed systems and security protocols such as OAuth and OpenID Connect, you will likely receive an RSA public key from another party, serialized as a JSON Web Key (JWK).
A JWK for an RSA public key looks like this:
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "334703f667abaaf9826239ce88c52856",
"n": "pgapT6UMCwD4x5df2XdgiaJTN4hFtTTjHruRwpqtdCdJijo3fYKtmbuT-xtqKvbaNtH_hkRGD_N8MULSXYTY8HZNfBgZkIvMyRz9gfu_Cu_TtxeeYZsnjnyK1IIXl1pfNOz9co7vq5PISgPW-6Mfsv1sUmFjNhOaA4hoH7gDTyEluo8lj-zswhVt9IFD-zhvlOYaN-4rUbVVy8-kGEhtDAC8kgv-w6XYUQ3a7tQFD3qjQkzxnMIE7zG-h21_CjoqTFQZfu6q1C9W1MIkGFzS9UZwoijPAwpk6OHwPruTg5hfFipktRf5E4DV3LKRF9kSg0ZM_YQF95oiQdDbdK-YS3qXErJqtO-Entep1pqluZS1BQgSqiIJ48-_5b46l_GZkWH2xvls46MlX5gxhkaES8DaWfKDmLaTYup3R-Y_EI3i4vDBMKoMBdS1JKRdoZxO9MY70qMLdQ0QcmgccC6i_PUCpbPvhQHp8lVgQ0T4_fopVCNIWJyaKj-kPN-jsNgh",
"e": "AQAB"
}
A JWK containing an RSA key will always have a kty
(key type) of “RSA” and an alg
(algorithm) value starting with “RS” when using the RSASSA-PKCS1-v1.5 scheme, or “PS” when using the RSASSA-PSS scheme.
The public key itself is contained in the n
(modulus) and e
(exponent) parameters.
A JWK can also contain an RSA private key. This has many more parameters, and while most parameters are officially “optimizations”, they are all required by .NET’s JWK libraries.
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "334703f667abaaf9826239ce88c52856",
"n": "pgapT6UMCwD4x5df2XdgiaJTN4hFtTTjHruRwpqtdCdJijo3fYKtmbuT-xtqKvbaNtH_hkRGD_N8MULSXYTY8HZNfBgZkIvMyRz9gfu_Cu_TtxeeYZsnjnyK1IIXl1pfNOz9co7vq5PISgPW-6Mfsv1sUmFjNhOaA4hoH7gDTyEluo8lj-zswhVt9IFD-zhvlOYaN-4rUbVVy8-kGEhtDAC8kgv-w6XYUQ3a7tQFD3qjQkzxnMIE7zG-h21_CjoqTFQZfu6q1C9W1MIkGFzS9UZwoijPAwpk6OHwPruTg5hfFipktRf5E4DV3LKRF9kSg0ZM_YQF95oiQdDbdK-YS3qXErJqtO-Entep1pqluZS1BQgSqiIJ48-_5b46l_GZkWH2xvls46MlX5gxhkaES8DaWfKDmLaTYup3R-Y_EI3i4vDBMKoMBdS1JKRdoZxO9MY70qMLdQ0QcmgccC6i_PUCpbPvhQHp8lVgQ0T4_fopVCNIWJyaKj-kPN-jsNgh",
"e": "AQAB",
"d": "DFWG2wafLisYkFVf1stOVfND_OZoDVX8QIJ9SeiNVcl8ZmM8T5v3cyoTDI7lFIOJwxRXSB2G2fUEBzHYaH-v0S9swrkUnx8vq8o2UCOIdhLKlvnPd59b8TMx6icvAvzBAXDQxGO3jPbZ5JvD6yZbesPmeflHslMC-Fu4JpEYV9bV03YCe56FmUaXyFgZGY8ABR8SGBEe0T9oPA-1OGObDE76s1vmRZkqyJy_pMoGBTgebFTEh1mHndCDf9Kj4zneX229WLgFIMAuUF6NsJIcvdoFQCM63saWQpI3JfbItYRqYEhjC5y7W-2y7DQGTNafMR8X38-GebhJvHCm4qiXzenbdQIHeEE85BWzHw9M2YlSZ8vMl3zQQMTh1vMZg07eYqwzr1zMYxI8HJAQKB8NR2VDe0XQS7vWouV4KTGvw4BLY0wNAy2vOKbyCEvyiBpYTrGYkpgjspJHbxJMGscq0vm90CsALvWnd4BpKoFBPiCBnshYddZAur6DKLBlNW6R",
"dp": "u5ZWFN_fbfkuUR7vAAAZudu6Wvn68HNgrHNgBilS4ohkUYdf8Mevt460kCmaZUxwjFByVOhULZhLE6zgtmr_Fo7tD-4pYR-ajetXWKYq5m1Kp7o4_BEqtG7XhUuzKWRpgnTQKB26gJSPaKK8diIq7ONb1N_6wwElC3-4H8fOLMlINtebuw26xX4LMSNFfvHlpf-7biHeTKi-pEPcWlSLSpYj1i1larIKTflY8B_VQvkdyhsG9m0R-nkahryKsANx",
"dq": "uSEU5laDj40xRSYu5s5uExtHIaP02YQMDlyhcYEj3W92xF8cUPvJ1DvpNlRDlDHxv8wN74wXyao7IpAgL7vBPTY1V9pbmPW_tdP3SISjkHp9UG0tEg8PQMQhAGh8bq7SFaTc2tRdiDJHKTYvFYFPIJuoLkmKA3K3THHyncsOk9fqOlaSTcQjmI27Z0f4wbt16xvIH_xAitwZ0j17HKv6bF62SNkDUjvVPd9t-3dVTvM4qq9yrtPrESpx206C16sh",
"p": "1dggd6aIYBRcND9f-V64zlspZdKWpSlIqZYX21ltw7i_agFp5pS2fwtkJErSKA20myUohNop5uMxQiTWJeb2nHe_OzGsjG68By_EmP4DEPdyPY-8RZ45wMHYdVvCbfNHCYxuUATbQXH8jMIiuBQq7BZyMUQ-iF3eTY5SzgdY3-ROny7mqCHMDq44XuT26r0p9IkM4Jg9RBlg8fdvbnIuSfqPdSUOPa4I2j8RFEOsVSs0teXNuUmOO-ovXGBwe-P5",
"q": "xsFWgVB6Av0zufrRGhLXfs5oBjg5Bg84qJbnuBSfOGTeHT8PLnu-G9YvwBxsq0KaoyRxLYS8cEPsqXj5eVk6qlzGeJwFHQr07UcpzgcQAgE7aWgLIp-q_BJ1rWg99msggvCHAI8F-WPS9SK1UT3vZqgWLzYiLKQJDgu0cf3fBCMVswHQenuiyRFyEqJYtlN4STZY2uVQQ3zXP0E53YHZjbwj7gO1O0uaDM1OVWfsTJiMXNUHW13Lt7CzlNgeWs9p",
"qi": "gTOcxTsCHkDuDxsePtg0cG40TKBwc72DDsGBJK1OBVVOLT7llQvMwWtRmiXtrkJLNtjSpvKN7taDy3gZQPwrJnUCL5w10orpNkU5-8_nK2tBp4kL7okuGMzr185Vh55NTCtLW02itqBm-_oMjG7CQ_92vQ7xH5BP56whX7naUGrmIqmDbf7cYwLAGf8GywRstrorzug9RHvQMYsiUqWfTY57rmxEH3ZIw-bJ5a2Pkmzb31qCXcX9uc-H0uLXCFLP"
}
Let’s see how to parse the JWKs in .NET.
Manually parsing RsaParameters in .NET
You can use a JWK to manually configure an RsaParameters
object.
To load the public key, you only need to set the Modulus
and Exponent
properties; however, to load the private key, you need to set every property.
const string json = "{\"alg\":\"RS256\",\"kty\":\"RSA\",\"use\":\"sig\",\"kid\":\"334703f667abaaf9826239ce88c52856\",\"n\":\"pgapT6UMCwD4x5df2XdgiaJTN4hFtTTjHruRwpqtdCdJijo3fYKtmbuT-xtqKvbaNtH_hkRGD_N8MULSXYTY8HZNfBgZkIvMyRz9gfu_Cu_TtxeeYZsnjnyK1IIXl1pfNOz9co7vq5PISgPW-6Mfsv1sUmFjNhOaA4hoH7gDTyEluo8lj-zswhVt9IFD-zhvlOYaN-4rUbVVy8-kGEhtDAC8kgv-w6XYUQ3a7tQFD3qjQkzxnMIE7zG-h21_CjoqTFQZfu6q1C9W1MIkGFzS9UZwoijPAwpk6OHwPruTg5hfFipktRf5E4DV3LKRF9kSg0ZM_YQF95oiQdDbdK-YS3qXErJqtO-Entep1pqluZS1BQgSqiIJ48-_5b46l_GZkWH2xvls46MlX5gxhkaES8DaWfKDmLaTYup3R-Y_EI3i4vDBMKoMBdS1JKRdoZxO9MY70qMLdQ0QcmgccC6i_PUCpbPvhQHp8lVgQ0T4_fopVCNIWJyaKj-kPN-jsNgh\",\"e\":\"AQAB\",\"d\":\"DFWG2wafLisYkFVf1stOVfND_OZoDVX8QIJ9SeiNVcl8ZmM8T5v3cyoTDI7lFIOJwxRXSB2G2fUEBzHYaH-v0S9swrkUnx8vq8o2UCOIdhLKlvnPd59b8TMx6icvAvzBAXDQxGO3jPbZ5JvD6yZbesPmeflHslMC-Fu4JpEYV9bV03YCe56FmUaXyFgZGY8ABR8SGBEe0T9oPA-1OGObDE76s1vmRZkqyJy_pMoGBTgebFTEh1mHndCDf9Kj4zneX229WLgFIMAuUF6NsJIcvdoFQCM63saWQpI3JfbItYRqYEhjC5y7W-2y7DQGTNafMR8X38-GebhJvHCm4qiXzenbdQIHeEE85BWzHw9M2YlSZ8vMl3zQQMTh1vMZg07eYqwzr1zMYxI8HJAQKB8NR2VDe0XQS7vWouV4KTGvw4BLY0wNAy2vOKbyCEvyiBpYTrGYkpgjspJHbxJMGscq0vm90CsALvWnd4BpKoFBPiCBnshYddZAur6DKLBlNW6R\",\"dp\":\"u5ZWFN_fbfkuUR7vAAAZudu6Wvn68HNgrHNgBilS4ohkUYdf8Mevt460kCmaZUxwjFByVOhULZhLE6zgtmr_Fo7tD-4pYR-ajetXWKYq5m1Kp7o4_BEqtG7XhUuzKWRpgnTQKB26gJSPaKK8diIq7ONb1N_6wwElC3-4H8fOLMlINtebuw26xX4LMSNFfvHlpf-7biHeTKi-pEPcWlSLSpYj1i1larIKTflY8B_VQvkdyhsG9m0R-nkahryKsANx\",\"dq\":\"uSEU5laDj40xRSYu5s5uExtHIaP02YQMDlyhcYEj3W92xF8cUPvJ1DvpNlRDlDHxv8wN74wXyao7IpAgL7vBPTY1V9pbmPW_tdP3SISjkHp9UG0tEg8PQMQhAGh8bq7SFaTc2tRdiDJHKTYvFYFPIJuoLkmKA3K3THHyncsOk9fqOlaSTcQjmI27Z0f4wbt16xvIH_xAitwZ0j17HKv6bF62SNkDUjvVPd9t-3dVTvM4qq9yrtPrESpx206C16sh\",\"p\":\"1dggd6aIYBRcND9f-V64zlspZdKWpSlIqZYX21ltw7i_agFp5pS2fwtkJErSKA20myUohNop5uMxQiTWJeb2nHe_OzGsjG68By_EmP4DEPdyPY-8RZ45wMHYdVvCbfNHCYxuUATbQXH8jMIiuBQq7BZyMUQ-iF3eTY5SzgdY3-ROny7mqCHMDq44XuT26r0p9IkM4Jg9RBlg8fdvbnIuSfqPdSUOPa4I2j8RFEOsVSs0teXNuUmOO-ovXGBwe-P5\",\"q\":\"xsFWgVB6Av0zufrRGhLXfs5oBjg5Bg84qJbnuBSfOGTeHT8PLnu-G9YvwBxsq0KaoyRxLYS8cEPsqXj5eVk6qlzGeJwFHQr07UcpzgcQAgE7aWgLIp-q_BJ1rWg99msggvCHAI8F-WPS9SK1UT3vZqgWLzYiLKQJDgu0cf3fBCMVswHQenuiyRFyEqJYtlN4STZY2uVQQ3zXP0E53YHZjbwj7gO1O0uaDM1OVWfsTJiMXNUHW13Lt7CzlNgeWs9p\",\"qi\":\"gTOcxTsCHkDuDxsePtg0cG40TKBwc72DDsGBJK1OBVVOLT7llQvMwWtRmiXtrkJLNtjSpvKN7taDy3gZQPwrJnUCL5w10orpNkU5-8_nK2tBp4kL7okuGMzr185Vh55NTCtLW02itqBm-_oMjG7CQ_92vQ7xH5BP56whX7naUGrmIqmDbf7cYwLAGf8GywRstrorzug9RHvQMYsiUqWfTY57rmxEH3ZIw-bJ5a2Pkmzb31qCXcX9uc-H0uLXCFLP\"}";
var parsedKey = JsonNode.Parse(json);
var rsaParameters = new RSAParameters
{
// PUBLIC KEY PARAMETERS
// n parameter - public modulus
Modulus = Base64UrlEncoder.DecodeBytes(parsedKey["n"].ToString()),
// e parameter - public exponent
Exponent = Base64UrlEncoder.DecodeBytes(parsedKey["e"].ToString()),
// PRIVATE KEY PARAMETERS
// d parameter - the private exponent value for the RSA key
D = Base64UrlEncoder.DecodeBytes(parsedKey["d"].ToString()),
// dp parameter - CRT exponent of the first factor
DP = Base64UrlEncoder.DecodeBytes(parsedKey["dp"].ToString()),
// dq parameter - CRT exponent of the second factor
DQ = Base64UrlEncoder.DecodeBytes(parsedKey["dq"].ToString()),
// p parameter - first prime factor
P = Base64UrlEncoder.DecodeBytes(parsedKey["p"].ToString()),
// q parameter - second prime factor
Q = Base64UrlEncoder.DecodeBytes(parsedKey["q"].ToString()),
// qi parameter - CRT coefficient of the second factor
InverseQ = Base64UrlEncoder.DecodeBytes(parsedKey["qi"].ToString()),
};
using var key = RSA.Create(rsaParameters);
JSON Web Keys base64url encode key parameters.
In this case, I’m using the base64url implementation found in Microsoft.IdentityModel.Tokens
.
An alternative is available in Microsoft.AspNetCore.Authentication
.
You can also use the ImportParameters
method on RSA
to load the parameters into an existing key.
Loading an RSA JWK using Microsoft.IdentityModel
Rather than manually parsing the JWK yourself, you could use the JsonWebKey
class found in Microsoft.IdentityModel.Tokens
.
const string json = "{\"alg\":\"RS256\",\"kty\":\"RSA\",\"use\":\"sig\",\"kid\":\"334703f667abaaf9826239ce88c52856\",\"n\":\"pgapT6UMCwD4x5df2XdgiaJTN4hFtTTjHruRwpqtdCdJijo3fYKtmbuT-xtqKvbaNtH_hkRGD_N8MULSXYTY8HZNfBgZkIvMyRz9gfu_Cu_TtxeeYZsnjnyK1IIXl1pfNOz9co7vq5PISgPW-6Mfsv1sUmFjNhOaA4hoH7gDTyEluo8lj-zswhVt9IFD-zhvlOYaN-4rUbVVy8-kGEhtDAC8kgv-w6XYUQ3a7tQFD3qjQkzxnMIE7zG-h21_CjoqTFQZfu6q1C9W1MIkGFzS9UZwoijPAwpk6OHwPruTg5hfFipktRf5E4DV3LKRF9kSg0ZM_YQF95oiQdDbdK-YS3qXErJqtO-Entep1pqluZS1BQgSqiIJ48-_5b46l_GZkWH2xvls46MlX5gxhkaES8DaWfKDmLaTYup3R-Y_EI3i4vDBMKoMBdS1JKRdoZxO9MY70qMLdQ0QcmgccC6i_PUCpbPvhQHp8lVgQ0T4_fopVCNIWJyaKj-kPN-jsNgh\",\"e\":\"AQAB\",\"d\":\"DFWG2wafLisYkFVf1stOVfND_OZoDVX8QIJ9SeiNVcl8ZmM8T5v3cyoTDI7lFIOJwxRXSB2G2fUEBzHYaH-v0S9swrkUnx8vq8o2UCOIdhLKlvnPd59b8TMx6icvAvzBAXDQxGO3jPbZ5JvD6yZbesPmeflHslMC-Fu4JpEYV9bV03YCe56FmUaXyFgZGY8ABR8SGBEe0T9oPA-1OGObDE76s1vmRZkqyJy_pMoGBTgebFTEh1mHndCDf9Kj4zneX229WLgFIMAuUF6NsJIcvdoFQCM63saWQpI3JfbItYRqYEhjC5y7W-2y7DQGTNafMR8X38-GebhJvHCm4qiXzenbdQIHeEE85BWzHw9M2YlSZ8vMl3zQQMTh1vMZg07eYqwzr1zMYxI8HJAQKB8NR2VDe0XQS7vWouV4KTGvw4BLY0wNAy2vOKbyCEvyiBpYTrGYkpgjspJHbxJMGscq0vm90CsALvWnd4BpKoFBPiCBnshYddZAur6DKLBlNW6R\",\"dp\":\"u5ZWFN_fbfkuUR7vAAAZudu6Wvn68HNgrHNgBilS4ohkUYdf8Mevt460kCmaZUxwjFByVOhULZhLE6zgtmr_Fo7tD-4pYR-ajetXWKYq5m1Kp7o4_BEqtG7XhUuzKWRpgnTQKB26gJSPaKK8diIq7ONb1N_6wwElC3-4H8fOLMlINtebuw26xX4LMSNFfvHlpf-7biHeTKi-pEPcWlSLSpYj1i1larIKTflY8B_VQvkdyhsG9m0R-nkahryKsANx\",\"dq\":\"uSEU5laDj40xRSYu5s5uExtHIaP02YQMDlyhcYEj3W92xF8cUPvJ1DvpNlRDlDHxv8wN74wXyao7IpAgL7vBPTY1V9pbmPW_tdP3SISjkHp9UG0tEg8PQMQhAGh8bq7SFaTc2tRdiDJHKTYvFYFPIJuoLkmKA3K3THHyncsOk9fqOlaSTcQjmI27Z0f4wbt16xvIH_xAitwZ0j17HKv6bF62SNkDUjvVPd9t-3dVTvM4qq9yrtPrESpx206C16sh\",\"p\":\"1dggd6aIYBRcND9f-V64zlspZdKWpSlIqZYX21ltw7i_agFp5pS2fwtkJErSKA20myUohNop5uMxQiTWJeb2nHe_OzGsjG68By_EmP4DEPdyPY-8RZ45wMHYdVvCbfNHCYxuUATbQXH8jMIiuBQq7BZyMUQ-iF3eTY5SzgdY3-ROny7mqCHMDq44XuT26r0p9IkM4Jg9RBlg8fdvbnIuSfqPdSUOPa4I2j8RFEOsVSs0teXNuUmOO-ovXGBwe-P5\",\"q\":\"xsFWgVB6Av0zufrRGhLXfs5oBjg5Bg84qJbnuBSfOGTeHT8PLnu-G9YvwBxsq0KaoyRxLYS8cEPsqXj5eVk6qlzGeJwFHQr07UcpzgcQAgE7aWgLIp-q_BJ1rWg99msggvCHAI8F-WPS9SK1UT3vZqgWLzYiLKQJDgu0cf3fBCMVswHQenuiyRFyEqJYtlN4STZY2uVQQ3zXP0E53YHZjbwj7gO1O0uaDM1OVWfsTJiMXNUHW13Lt7CzlNgeWs9p\",\"qi\":\"gTOcxTsCHkDuDxsePtg0cG40TKBwc72DDsGBJK1OBVVOLT7llQvMwWtRmiXtrkJLNtjSpvKN7taDy3gZQPwrJnUCL5w10orpNkU5-8_nK2tBp4kL7okuGMzr185Vh55NTCtLW02itqBm-_oMjG7CQ_92vQ7xH5BP56whX7naUGrmIqmDbf7cYwLAGf8GywRstrorzug9RHvQMYsiUqWfTY57rmxEH3ZIw-bJ5a2Pkmzb31qCXcX9uc-H0uLXCFLP\"}";
var jsonWebKey = new JsonWebKey(json);
// var rsaParameters = jsonWebKey.CreateRsaParameters(); // is internal 😭
var rsaParameters = new RSAParameters
{
// PUBLIC KEY PARAMETERS
// n parameter - public modulus
Modulus = Base64UrlEncoder.DecodeBytes(jsonWebKey.N),
// e parameter - public exponent
Exponent = Base64UrlEncoder.DecodeBytes(jsonWebKey.E),
// PRIVATE KEY PARAMETERS (optional)
// d parameter - the private exponent value for the RSA key
D = Base64UrlEncoder.DecodeBytes(jsonWebKey.D),
// dp parameter - CRT exponent of the first factor
DP = Base64UrlEncoder.DecodeBytes(jsonWebKey.DP),
// dq parameter - CRT exponent of the second factor
DQ = Base64UrlEncoder.DecodeBytes(jsonWebKey.DQ),
// p parameter - first prime factor
P = Base64UrlEncoder.DecodeBytes(jsonWebKey.P),
// q parameter - second prime factor
Q = Base64UrlEncoder.DecodeBytes(jsonWebKey.Q),
// qi parameter - CRT coefficient of the second factor
InverseQ = Base64UrlEncoder.DecodeBytes(jsonWebKey.QI)
};
using var key = RSA.Create(rsaParameters);
JsonWebKey
implements SecurityKey
, meaning you can use it with JsonWebTokenHandler
.
var result = new JsonWebTokenHandler().ValidateToken(
jwt,
new TokenValidationParameters
{
ValidIssuer = "me",
ValidAudience = "you",
IssuerSigningKey = jsonWebKey
});
Unfortunately, the methods it uses to parse the RSA
or RsaSecurityKey
objects are internal.
Still, its parsing and validation of the JWK can be useful, saving you from loading properties yourself and validating private key presence with the HasPrivateKey
method.
Loading an RSA key from an OpenSSL PEM file in .NET
To load an RSA key from a PEM file, you can use the ImportFromPem
method introduced in .NET 5.
This method handles the PKCS #1 and PKCS #8 formats generated by OpenSSL.
using var key = RSA.Create();
key.ImportFromPem(File.ReadAllText("key-pkcs1.pem"));
This method saves you from parsing and validating PEM headers and footers or handling any decoding, which you had to do in older versions of .NET.
Loading an RSA key from an X509 certificate in .NET
To load an RSA key from an X509Certificate2
object, you can use the GetRSAPrivateKey
or GetRSAPublicKey
methods which return an instance of RSA
.
You can use the HasPrivateKey
method to decide which method to use.
var cert = new X509Certificate2("cert.pfx");
using var key = cert.GetRSAPrivateKey() ?? cert.GetRSAPublicKey();
GetRSAPrivateKey
will return null if the certificate does not contain a copy of the private key.
While you could use the PrivateKey
property, this is now marked as obsolete as of .NET 6.
Source code
You can find sample code for the above approaches in my GitHub samples repository. These samples are tested against .NET 6 onwards and run on both Windows and Linux.
To learn more about RSA usage in .NET, check out some of my other articles: