Skip to content
ecdsa.com

Ed25519 sign & verify · .NET

How to sign and verify Ed25519 signatures in C#

System.Security.Cryptography in .NET 10 still has no Ed25519, so C# code signs and verifies Ed25519 signatures with a library; NSec.Cryptography, a thin wrapper over libsodium, is the most direct one. It imports the same PEM files as OpenSSL and produces the deterministic 64-byte RFC 8032 signature.

Tested with NSec.Cryptography 26.4.0 (.NET SDK 10.0.402, libsodium).

app.cs — import a PKCS#8 PEM key and sign (a .NET 10 file-based app)
#:package NSec.Cryptography@26.4.0
using System.Text;
using NSec.Cryptography;
// .NET's System.Security.Cryptography has no Ed25519; NSec wraps libsodium
var algorithm = SignatureAlgorithm.Ed25519;
using var privateKey = Key.Import(algorithm, File.ReadAllBytes("key.pem"), KeyBlobFormat.PkixPrivateKeyText);
byte[] message = File.ReadAllBytes("message.txt");
byte[] signature = algorithm.Sign(privateKey, message);
File.WriteAllBytes("sig.bin", signature);
Console.WriteLine($"signature: {Convert.ToHexStringLower(signature)}");
verify with the SPKI public key
var publicKey = PublicKey.Import(algorithm, File.ReadAllBytes("pub.pem"), KeyBlobFormat.PkixPublicKeyText);
Console.WriteLine($"valid: {algorithm.Verify(publicKey, message, signature)}");
Console.WriteLine($"tampered message valid: {algorithm.Verify(publicKey, Encoding.UTF8.GetBytes("hello ed25520"), signature)}");

How it works

  1. #:package NSec.Cryptography@26.4.0 lets dotnet run app.cs restore the package without a project file; in a project, add the same version with dotnet add package.
  2. Key.Import(algorithm, bytes, KeyBlobFormat.PkixPrivateKeyText) reads BEGIN PRIVATE KEY, and PublicKey.Import(…, KeyBlobFormat.PkixPublicKeyText) reads BEGIN PUBLIC KEY. The RawPrivateKey and RawPublicKey formats take the bare 32 bytes instead.
  3. algorithm.Sign(key, message) returns 64 bytes; algorithm.Verify(publicKey, message, signature) returns a bool.
  4. Key holds the private key in unmanaged memory — dispose it (using var) when done.

Gotchas

  • Do not look for it in the platform: .NET 10 added MLDsa, SlhDsa and MLKem to System.Security.Cryptography, but no Ed25519 type, and ECDsa only covers Weierstrass curves such as P-256.
  • Imported keys are not exportable by default: key.Export(KeyBlobFormat.RawPrivateKey) throws InvalidOperationException: The key cannot be exported. Pass new KeyCreationParameters { ExportPolicy = KeyExportPolicies.AllowPlaintextExport } to Key.Import when you need it.
  • A PEM of another key type, such as a P-256 key, throws FormatException: The key BLOB is not in the correct format.; Key.TryImport returns false instead.
  • NSec needs the native libsodium that its NuGet dependency ships for the usual Windows, Linux and macOS targets. Where native code is not an option, BouncyCastle.Cryptography's Ed25519Signer is the fully managed alternative.

Related recipes