.NET Core 3.0 introduced over a dozen new APIs for importing and exporting RSA keys in different formats. Many of them are a variant of another with a slightly different API, but they are extremely useful for working with private and public keys from other systems that work with encoding keys.
RSA keys can be encoded in a variety of different ways, depending on if the key is public or private or protected with a password. Different programs will import or export RSA keys in a different format, etc.
Often times RSA keys can be described as “PEM” encoded, but that is already ambiguous as to how the key is actually encoded. PEM takes the form of:
-----BEGIN LABEL----- content -----END LABEL-----
The content between the labels is base64 encoded. The one that is probably the most often seen is BEGIN RSA PRIVATE KEY, which is frequently used in web servers like nginx, apache, etc:
-----BEGIN RSA PRIVATE KEY----- MII... -----END RSA PRIVATE KEY-----
The base64-encoded text is an RSAPrivateKey from the PKCS#1 spec, which is
just an ASN.1 SEQUENCE of integers that make up the RSA key. The corresponding
.NET Core 3 API for this is
ImportRSAPrivateKey, or one of its overloads.
If your key is “PEM” encoded, you need to find the base64 text between the label
BEGIN and END headers, base64 decode it, and pass to
There is currently an API proposal to make reading PEM files easier.
If your private key is DER encoded, then that just means you can read the
content directly as bytes in to
Here is an example:
var privateKey = "MII..."; //Get just the base64 content. var privateKeyBytes = Convert.FromBase64String(privateKey); using var rsa = RSA.Create(); rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
When using openssl, the
openssl rsa commands typically output RSAPrivateKey
PKCS#1 private keys, for example
A different format for a private key is PKCS#8. Unlike the RSAPrivateKey from PKCS#1, a PKCS#8 encoded key can represent other kinds of keys than RSA. As such, the PEM label for a PKCS#8 key is “BEGIN PRIVATE KEY” (note the lack of “RSA” there). The key itself contains an AlgorithmIdentifer of what kind of key it is.
PKCS#8 keys can also be encrypted protected, too. In that case, the PEM label will be “BEGIN ENCRYPTED PRIVATE KEY”.
.NET Core 3 has APIs for both of these. Unencrypted PKCS#8 keys can be imported
ImportPkcs8PrivateKey, and encrypted PKCS#8 keys can be imported with
ImportEncryptedPkcs8PrivateKey. Their usage is similar to
Public keys have similar behavior. A PEM encoded key that has the label
“BEGIN RSA PUBLIC KEY” should use
ImportRSAPublicKey. Also like private keys,
the public key has a format that self-describes the algorithm of the key called
a Subject Public Key Info (SPKI) which is used heavily in X509 and many other
standards. The PEM header for this is “BEGIN PUBLIC KEY”, and
ImportSubjectPublicKeyInfo is the correct way to import these.
All of these APIs have export versions of themselves as well, so if you are trying to export a key from .NET Core 3 to a particular format, you’ll need to use the correct export API.
To summarize each PEM label and API pairing:
- “BEGIN RSA PRIVATE KEY” =>
- “BEGIN PRIVATE KEY” =>
- “BEGIN ENCRYPTED PRIVATE KEY” =>
- “BEGIN RSA PUBLIC KEY” =>
- “BEGIN PUBLIC KEY” =>
One gotcha with openssl is to pay attention to the output of the key format. A common enough task from openssl is “Given this PEM-encoded RSA private key, give me a PEM encoded public-key” and is often enough done like this:
openssl rsa -in key.pem -pubout
Even if key.pem is a PKCS#1 RSAPrivateKey (“BEGIN RSA PRIVATE KEY”), the
option will output a SPKI (“BEGIN PUBLIC KEY”), not an RSAPublicKey
(“BEGIN RSA PUBLIC KEY”). For that, you would need to use
-pubout. The openssl
pkey commands will also typically give you
PKCS#8 or SPKI formatted keys.