Skip to content
ecdsa.com

Public key from a certificate · .NET

How to extract the public key from an X.509 certificate in C# (.NET)

To extract the public key from an X.509 certificate in C#, call cert.PublicKey.ExportSubjectPublicKeyInfo() and wrap the bytes with PemEncoding.Write("PUBLIC KEY", …). This recipe does that on .NET 10, computes the key's SHA-256 fingerprint and base64 pin, and checks a private key against the certificate with CopyWithPrivateKey.

Tested with .NET 10.0.13 (SDK 10.0.402) (System.Security.Cryptography.X509Certificates).

extract.cs — SPKI PEM, fingerprint, pin and the key-pair check
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using X509Certificate2 cert = X509CertificateLoader.LoadCertificateFromFile("cert.pem"); // PEM or DER
// PublicKey works for every algorithm; ExportSubjectPublicKeyInfo returns the SPKI DER
byte[] spki = cert.PublicKey.ExportSubjectPublicKeyInfo();
string pem = new string(PemEncoding.Write("PUBLIC KEY", spki)) + "\n";
Console.Write(pem);
byte[] digest = SHA256.HashData(spki);
Console.WriteLine($"SPKI SHA-256: {Convert.ToHexStringLower(digest)}");
Console.WriteLine($"SPKI pin (base64): {Convert.ToBase64String(digest)}");
// Typed access when you know the algorithm: null for other key types
using ECDsa? ecdsa = cert.GetECDsaPublicKey();
Console.WriteLine($"same as GetECDsaPublicKey(): {ecdsa!.ExportSubjectPublicKeyInfoPem() + "\n" == pem}");
// Does key.pem belong to this certificate? CopyWithPrivateKey refuses a key that does not match.
using ECDsa privateKey = ECDsa.Create();
privateKey.ImportFromPem(File.ReadAllText("key.pem"));
using X509Certificate2 withKey = cert.CopyWithPrivateKey(privateKey);
Console.WriteLine($"key.pem matches: {withKey.HasPrivateKey}");

How it works

  1. cert.PublicKey.ExportSubjectPublicKeyInfo() returns the SubjectPublicKeyInfo DER for any algorithm; PemEncoding.Write("PUBLIC KEY", spki) turns it into the standard PEM.
  2. SHA-256 of the SPKI DER is the key fingerprint (45caa7e3…7b5f), and its base64 (Rcqn43J0…e18=) the SPKI pin.
  3. When you know the algorithm, GetECDsaPublicKey() (or GetRSAPublicKey()) gives a typed key whose ExportSubjectPublicKeyInfoPem() produces the same PEM.
  4. cert.CopyWithPrivateKey(key) checks that the private key matches the certificate's public key; on success the copy has HasPrivateKey == True.

Gotchas

  • cert.GetPublicKey() is not the SPKI: it returns only the bytes inside the BIT STRING — the bare 65-byte EC point starting with 04 here. Hashing it gives a value no pinning library expects.
  • CopyWithPrivateKey with a key that does not belong to the certificate throws ArgumentException: The provided key does not match the public key for this certificate. — catch it if you use it as a check.

Related recipes