Skip to content
ecdsa.com

Export public key PEM · .NET

How to export a public key as PEM in C# (.NET)

To export a public key as PEM in C#, two calls are enough: ECDsa.ImportFromPem loads the private key, ExportSubjectPublicKeyInfoPem writes the public half as a standard BEGIN PUBLIC KEY block. No derivation code is needed — the imported key already knows its public point — and the same ECParameters give you the JWK form.

Tested with .NET 10.0.13 (SDK 10.0.402) (Linux, OpenSSL 3.0.13 backend) and OpenSSL 3.0.13 (openssl pkey cross-check).

export.cs — private key PEM in, SPKI PEM out
using System.Security.Cryptography;
using var key = ECDsa.Create();
key.ImportFromPem(File.ReadAllText("key.pem")); // PKCS#8 or SEC 1: the public point comes along
string pem = key.ExportSubjectPublicKeyInfoPem(); // "BEGIN PUBLIC KEY", no trailing newline
File.WriteAllText("pub.pem", pem + "\n");
Console.WriteLine(pem);
the same public key as a JWK
// The same public key as a JWK: the point's coordinates, base64url without padding (.NET 9+)
ECParameters point = key.ExportParameters(includePrivateParameters: false);
string x = System.Buffers.Text.Base64Url.EncodeToString(point.Q.X);
string y = System.Buffers.Text.Base64Url.EncodeToString(point.Q.Y);
Console.WriteLine($$"""{"kty":"EC","crv":"P-256","x":"{{x}}","y":"{{y}}"}""");

How it works

  1. ImportFromPem accepts BEGIN PRIVATE KEY (PKCS#8) and BEGIN EC PRIVATE KEY (SEC 1); the run feeds it both files and gets byte-identical output.
  2. ExportSubjectPublicKeyInfoPem() (.NET 7+) writes SPKI with the curve OID, wrapped at 64 columns — the run compares it byte for byte with openssl pkey -pubout.
  3. ExportSubjectPublicKeyInfo() returns the same structure as DER bytes, the input for fingerprints and for ImportSubjectPublicKeyInfo elsewhere.
  4. ExportParameters(false) exposes the public point; Q.X and Q.Y are fixed-width big-endian coordinates, so base64url-encoding them directly gives valid JWK x and y values.

Gotchas

  • ExportSubjectPublicKeyInfoPem() returns the PEM without a final newline. Add one before writing a file, or concatenating PEM files and comparing them with other tools' output fails.
  • ExportParameters(true) includes the private scalar D. Pass false whenever the result leaves the process — a JWK built from the wrong call publishes the private key.
  • ImportFromPem wants the PEM text. Given a file path, or a certificate, it fails with No supported key formats were found; for a certificate use X509CertificateLoader and GetECDsaPublicKey().
  • ExportRSAPublicKeyPem and the other Export…Pem methods write different containers. For ECDSA keys only SPKI (BEGIN PUBLIC KEY) is universally understood.

Related recipes