Skip to content
ecdsa.com

Generate a key pair · .NET

How to generate an ECDSA key pair in C# (.NET)

To generate an ECDSA key pair in C#, call ECDsa.Create with a named curve; since .NET 7 the key exports straight to the standard PEM files, ExportPkcs8PrivateKeyPem for the private half and ExportSubjectPublicKeyInfoPem for the public half. This recipe writes both, protects the private key file, and adds a password-encrypted variant OpenSSL can open.

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).

generate.cs — a P-256 key pair written as PEM files
using System.Security.Cryptography;
using var key = ECDsa.Create(ECCurve.NamedCurves.nistP256); // P-256 on Windows, Linux and macOS
// a new file (never overwrite a key), readable by its owner only: mode 600
var options = new FileStreamOptions { Mode = FileMode.CreateNew, Access = FileAccess.Write };
if (!OperatingSystem.IsWindows()) // Windows protects files with ACLs instead
{
options.UnixCreateMode = UnixFileMode.UserRead | UnixFileMode.UserWrite;
}
using (var writer = new StreamWriter("key.pem", options))
{
writer.WriteLine(key.ExportPkcs8PrivateKeyPem()); // "BEGIN PRIVATE KEY"
}
File.WriteAllText("pub.pem", key.ExportSubjectPublicKeyInfoPem() + "\n"); // "BEGIN PUBLIC KEY"
Console.Write(File.ReadAllText("pub.pem"));
a password-protected private key, and reading it back
// "BEGIN ENCRYPTED PRIVATE KEY": PKCS#8 under PBES2 (AES-256-CBC, PBKDF2-HMAC-SHA256)
string password = Environment.GetEnvironmentVariable("KEY_PASSWORD")!;
var pbe = new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, iterationCount: 600_000);
File.WriteAllText("key-encrypted.pem", key.ExportEncryptedPkcs8PrivateKeyPem(password, pbe) + "\n");
// reading it back
using var reloaded = ECDsa.Create();
reloaded.ImportFromEncryptedPem(File.ReadAllText("key-encrypted.pem"), password);

How it works

  1. ECDsa.Create(ECCurve.NamedCurves.nistP256) generates the key immediately; nistP384 and nistP521 select the larger NIST curves.
  2. ExportPkcs8PrivateKeyPem() returns BEGIN PRIVATE KEY text and ExportSubjectPublicKeyInfoPem() returns BEGIN PUBLIC KEY; the run checks with openssl pkey that the two files belong together.
  3. FileMode.CreateNew refuses to overwrite an existing key, and on Linux and macOS UnixCreateMode creates the file with mode 600 before any key material is written.
  4. ExportEncryptedPkcs8PrivateKeyPem wraps the key in PBES2 with the chosen cipher, hash and PBKDF2 iteration count; ImportFromEncryptedPem reverses it, and OpenSSL opens the same file with -passin.

Gotchas

  • The PEM export methods return text without a trailing newline. Tools that concatenate PEM files, and cmp against OpenSSL output, need the final \n added as above.
  • On Linux, .NET delegates to the system OpenSSL, so curve support follows the platform: friendly names like "P-256" throw PlatformNotSupportedException there, while the ECCurve.NamedCurves members work everywhere.
  • UnixCreateMode throws on Windows, hence the OperatingSystem.IsWindows() check — which also keeps the platform analyzer (CA1416) quiet. On Windows, restrict the file with ACLs or keep keys in the certificate store.
  • ECDsa objects wrap native handles. Without using, private key material stays in unmanaged memory until the finalizer runs.

Related recipes