Secp256k1.Net
Cross platform C# wrapper for the native secp256k1 lib (netstandard2.0)
Install / Use
/learn @zone117x/Secp256k1.NetREADME
Secp256k1.Net
Cross platform C# wrapper for the native bitcoin-core/secp256k1 C library.
dotnet add package Secp256k1.Net
Platform Support
Pre-compiled binaries are bundled for the following platforms:
| OS | x64 | x86 | arm64 | |----|:---:|:---:|:-----:| | Windows | ✓ | ✓ | ✓ | | Linux (glibc) | ✓ | ✓ | ✓ | | Linux (musl/Alpine) | ✓ | | ✓ | | macOS | ✓ | | ✓ |
This library targets netstandard2.0 and net8.0, supporting a wide-range of .NET deployments: .NET Core 2.0+, .NET Framework 4.6.1+, Mono 5.4+, etc. Conditional compilation is used to enable optimized native library interop features available on modern targets (net8.0 and above).
Quick Start
using Secp256k1Net;
using System.Security.Cryptography;
using System.Text;
// Generate a key pair
var (secretKey, publicKey) = Secp256k1.CreateKeyPair(compressed: true);
// Sign a message (ECDSA)
byte[] message = SHA256.HashData(Encoding.UTF8.GetBytes("Hello, secp256k1!"));
byte[] signature = Secp256k1.Sign(message, secretKey);
bool isValid = Secp256k1.Verify(signature, message, publicKey);
// Schnorr signatures (BIP-340)
var (xOnlyPubKey, _) = Secp256k1.CreateXOnlyPublicKey(secretKey);
byte[] schnorrSig = Secp256k1.SignSchnorr(message, secretKey);
bool schnorrValid = Secp256k1.VerifySchnorr(schnorrSig, message, xOnlyPubKey);
// ECDH shared secret
var (aliceSecret, alicePublic) = Secp256k1.CreateKeyPair(compressed: true);
var (bobSecret, bobPublic) = Secp256k1.CreateKeyPair(compressed: true);
byte[] sharedSecret1 = Secp256k1.ComputeSharedSecret(bobPublic, aliceSecret);
byte[] sharedSecret2 = Secp256k1.ComputeSharedSecret(alicePublic, bobSecret);
// sharedSecret1 == sharedSecret2
See the examples project for more complete working examples.
API Reference
The Secp256k1 class exposes static functions that are idiomatic C#, using a thread-safe internal context:
Key Generation & Validation
CreateSecretKey()- Generate a cryptographically secure random secret key (example)CreatePublicKey(secretKey, compressed)- Derive a serialized public key from a secret key (example)CreateXOnlyPublicKey(secretKey)- Derive an x-only public key and parity for BIP-340 (example)CreateKeyPair(compressed)- Generate a new secret key and public key pair (example)IsValidSecretKey(secretKey)- Validate a secret key (example)IsValidPublicKey(publicKey)- Validate a serialized public key (example)
Public Key Operations
CompressPublicKey(publicKey)- Convert a public key to 33-byte compressed format (example)DecompressPublicKey(publicKey)- Convert a public key to 65-byte uncompressed format (example)NegatePublicKey(publicKey, compressed)- Negate a public key (example)CombinePublicKeys(publicKeys, compressed)- Add multiple public keys together (example)
ECDSA Signing & Verification
Sign(messageHash, secretKey)- Create a 64-byte compact ECDSA signature (example)Verify(signature, messageHash, publicKey)- Verify an ECDSA signature (example)SignRecoverable(messageHash, secretKey)- Create a recoverable signature with recovery ID (example)RecoverPublicKey(signature, recoveryId, messageHash, compressed)- Recover public key from signature (example)
DER Signature Format
SignatureToDer(compactSignature)- Convert compact signature to DER format (example)SignatureFromDer(derSignature)- Convert DER signature to compact format (example)VerifyDer(derSignature, messageHash, publicKey)- Verify a DER-encoded signature (example)
Signature Normalization
NormalizeSignature(signature)- Normalize signature to lower-S form (example)IsNormalizedSignature(signature)- Check if signature is in lower-S form (example)
Schnorr Signatures (BIP-340)
SignSchnorr(messageHash, secretKey, auxRand)- Create a Schnorr signature (example)VerifySchnorr(signature, message, publicKey)- Verify a Schnorr signature (example)
ECDH Key Agreement
ComputeSharedSecret(publicKey, secretKey)- Compute ECDH shared secret (example)
Key Tweaking (BIP-32 HD Wallets)
TweakSecretKeyAdd(secretKey, tweak)- Add a tweak to a secret key (example)TweakPublicKeyAdd(publicKey, tweak, compressed)- Add a tweak to a public key (example)TweakSecretKeyMul(secretKey, tweak)- Multiply a secret key by a tweak (example)TweakPublicKeyMul(publicKey, tweak, compressed)- Multiply a public key by a tweak (example)NegateSecretKey(secretKey)- Negate a secret key (example)
Hashing
TaggedHash(tag, message)- Compute a BIP-340 tagged hash (example)
Advanced Usage
The Secp256k1 class also provides instance methods that are direct wrappers for the native C library, with near one-to-one API mapping. These offer more control over memory allocation and access to additional features:
- Custom ECDH hash functions - Use custom hash functions for ECDH
- Custom nonce functions - Provide custom nonce generation for signing
- Public key sorting - Sort public keys lexicographically
- Keypair operations - Work with 96-byte keypair objects
- X-only pubkey tweaking - Taproot-style key tweaking (BIP-341)
- ElligatorSwift encoding - BIP-324 encrypted transport
- MuSig2 multi-signatures - Aggregate Schnorr signatures from multiple signers
Benchmarks
Secp256k1.Net is consistently 5-10x faster than the next best library (NBitcoin) and 20-100x faster than pure managed implementations like BouncyCastle, Nethereum, and StarkBank.
BenchmarkDotNet v0.15.8, macOS Sequoia 15.7.1 (24G231) [Darwin 24.6.0]
Apple M3 Max, 1 CPU, 14 logical and 14 physical cores
.NET SDK 10.0.102
[Host] : .NET 10.0.2 (10.0.2, 10.0.225.61305), Arm64 RyuJIT armv8.0-a
DefaultJob : .NET 10.0.2 (10.0.2, 10.0.225.61305), Arm64 RyuJIT armv8.0-a
| Method | Categories | Mean | Error | StdDev | Ratio | RatioSD | |------------- |--------------------- |-------------:|-----------:|-----------:|-------:|--------:| | Secp256k1Net | Ecdh | 22.964 μs | 0.1813 μs | 0.1607 μs | 1.00 | 0.01 | | NBitcoin | Ecdh | 167.133 μs | 0.6087 μs | 0.5694 μs | 7.28 | 0.05 | | Nethereum | Ecdh | 500.696 μs | 3.4009 μs | 3.1812 μs | 21.80 | 0.20 | | BouncyCastle | Ecdh | 503.882 μs | 6.4419 μs | 6.0257 μs | 21.94 | 0.29 | | | | | | | | | | Secp256k1Net | EcdsaRecover | 36.042 μs | 0.1504 μs | 0.1333 μs | 1.00 | 0.01 | | NBitcoin | EcdsaRecover | 268.565 μs | 1.1492 μs | 1.0187 μs | 7.45 | 0.04 | | Nethereum | EcdsaRecover | 1,977.580 μs | 14.0846 μs | 12.4856 μs | 54.87 | 0.39 | | BouncyCastle | EcdsaRecover | 2,270.418 μs | 27.8990 μs | 26.0967 μs | 62.99 | 0.74 | | | | | | | | | | Secp256k1Net | EcdsaSign | 15.231 μs | 0.0491 μs | 0.0436 μs | 1.00 | 0.00 | | NBitcoin | EcdsaSign | 133.297 μs | 0.5326 μs | 0.4721 μs | 8.75 | 0.04 | | Nethereum | EcdsaSign | 319.805 μs | 2.8822 μs | 2.6960 μs | 21.00 | 0.18 | | BouncyCastle | EcdsaSign
