Skip to content
ecdsa.com

Verify a signature · .NET

How to verify an ECDSA signature in C# (.NET)

To verify an ECDSA signature in C#, .NET's System.Security.Cryptography needs no NuGet package: ECDsa.ImportFromPem loads the key and VerifyData checks the signature. The trap is the default signature format — .NET expects raw r‖s (IEEE P1363), so a DER signature from OpenSSL or Java needs DSASignatureFormat.Rfc3279DerSequence, or it simply verifies as false.

Tested with .NET 10.0.13 (SDK 10.0.402) (Linux, OpenSSL 3.0.13 backend).

verify.cs — verify a DER signature (OpenSSL, Java, Go)
using System.Security.Cryptography;
using var publicKey = ECDsa.Create();
publicKey.ImportFromPem(File.ReadAllText("pub.pem")); // "BEGIN PUBLIC KEY" (SPKI)
byte[] message = File.ReadAllBytes("message.txt");
byte[] signature = File.ReadAllBytes("sig.der"); // ASN.1 DER, as OpenSSL, Java and Go produce
bool valid = publicKey.VerifyData(message, signature, HashAlgorithmName.SHA256,
DSASignatureFormat.Rfc3279DerSequence); // the default would be raw r‖s
Console.WriteLine(valid ? "valid" : "INVALID");
raw r‖s signatures use the default format
// r‖s from a JWS, WebCrypto or a hardware token: .NET's default format, no flag needed
byte[] raw = File.ReadAllBytes("sig.raw"); // 64 bytes on P-256
Console.WriteLine(publicKey.VerifyData(message, raw, HashAlgorithmName.SHA256) ? "valid" : "INVALID");

How it works

  1. ImportFromPem reads the PEM label and picks the decoder: PUBLIC KEY (SPKI), PRIVATE KEY (PKCS#8) or EC PRIVATE KEY (SEC 1). The curve comes from the key.
  2. VerifyData hashes the message itself — pass the original bytes and the hash name, not a digest. For a precomputed digest there is VerifyHash.
  3. DSASignatureFormat.Rfc3279DerSequence selects ASN.1 DER. Without it, .NET uses IeeeP1363FixedFieldConcatenation, the 64-byte r‖s of JWS and WebCrypto.
  4. ECDsa holds native resources: using var disposes the key handle when the scope ends.

Gotchas

  • A signature in the wrong format is not an error in .NET: a DER signature checked with the default format returns false (the run shows it), exactly like a forged one. When a valid-looking signature fails, check the format first.
  • ImportFromPem takes the PEM text, not a path. ImportFromPem("pub.pem") throws No supported key formats were found — the same error a certificate PEM produces; load certificates with X509CertificateLoader and call GetECDsaPublicKey().
  • A file with two keys throws The input contains multiple keys, and an encrypted private key needs ImportFromEncryptedPem with its password.
  • Curves are platform-dependent: on Linux, .NET calls OpenSSL and on Windows, CNG. ECCurve.NamedCurves.nistP256 works everywhere, while a friendly name such as "P-256" throws PlatformNotSupportedException on Linux.

Related recipes