跳到主要内容
ecdsa.com
.NET

Bouncy Castle for .NET (BouncyCastle.Cryptography)

面向 .NET 的 Bouncy Castle,.NET 开发者用来补齐基类库所缺功能的托管密码学库:Ed25519 和 Ed448、在所有平台上都可用的 secp256k1、确定性 ECDSA、PGP、PEM,以及不依赖操作系统的 ML-KEM、ML-DSA 和 SLH-DSA。
许可证
MIT
检查的版本
2.7.0 ·
源代码
github.com/bcgit/bc-csharp
文档
www.bouncycastle.org/documentation/documentation-c/

兼容性概览

ECDSAEdDSA后量子

ECDSA 曲线
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
后量子算法
ML-KEMML-DSASLH-DSA
公钥格式
spki-pemspki-der
私钥格式
pkcs8-dersec1-pem

BouncyCastle.Cryptography(取代 Portable.BouncyCastle 的包)是纯托管代码:结果不取决于 Windows CNG 或 Linux 上的 OpenSSL 版本,这正是它与 System.Security.Cryptography 搭配使用的价值所在。它提供基于字符串的 SignerUtilities API,以及 ECDsaSigner 等更底层的类。

它的 ECDSA 默认行为遵循 OpenSSL 的传统,而不是 .NET 的:SHA-256withECDSA 生成 DER,而 SHA-256withPLAIN-ECDSA 给出 .NET 自带的 ECDsa 和 JWS 所用的 64 字节原始 r‖s。在同一个应用中同时使用这两个库时,把这两种格式弄混是最常见的互操作故障;所有格式错误的输入都返回 false,而不会抛出异常。通过 SignerUtilities 签名时 nonce 是随机的;ECDsaSigner 搭配 HMacDsaKCalculator 复现了 RFC 6979 的测试向量。s 不做规范化,验证时接受 high-S。

人们引入它,看重的正是算法覆盖面:P-224 到 P-521、secp256k1 和 Brainpool r1 曲线;Ed25519 和 Ed448(如示例所示);以及通过 MLKemEncapsulator、MLDsaSigner 和 SlhDsaSigner 提供的 ML-KEM、ML-DSA 和 SLH-DSA——基类库在同一个 Linux 镜像上报告完全不支持后量子算法,而这些在该镜像上都能正常工作。可以在后量子签名工具中试用这些算法。

PemWriter 把 EC 私钥写成 SEC1 格式的 -----BEGIN EC PRIVATE KEY-----,PemReader 则把它读回为 AsymmetricCipherKeyPair;PKCS#8 需通过 PrivateKeyInfoFactory 处理。所有 2.x 版本的程序集自身版本号都是强名称 2.0.0,因此比较行为时要查看包版本(这里是 2.7.0)。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/bouncy-castle-csharp/run.sh 于2026年10月20日在容器镜像 mcr.microsoft.com/dotnet/sdk:10.0.402-noble 中运行了 Bouncy Castle for .NET (BouncyCastle.Cryptography) 2.7.0;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • ECDSA 签名编码

    der

    SHA-256withECDSA 返回 DER;SHA-256withPLAIN-ECDSA 返回 64 字节的原始 r‖s。

    检查记录
    fact ecdsa-signature-encoding = der
    evidence ecdsa-signature-encoding: SHA-256withPLAIN-ECDSA output: 64 bytes
  • ECDSA 的 nonce

    rfc6979-opt-in

    通过 SignerUtilities 时是随机的;ECDsaSigner 搭配 HMacDsaKCalculator 复现了 RFC 6979 附录 A.2.5。

    检查记录
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: SHA-256withECDSA repeats: False; ECDsaSigner with HMacDsaKCalculator reproduces RFC 6979 A.2.5: True
  • 签名时的 low-S

    either

    s 没有经过规范化:64 个签名中出现了 high-S 值。

    检查记录
    fact ecdsa-low-s-sign = either
  • 验证时的 high-S

    accepts-high-s

    把 s 替换为 n − s 的 DER 签名验证通过。

    检查记录
    fact ecdsa-low-s-verify = accepts-high-s
  • 签名调用对哪些内容做哈希

    hashes-message-or-digest

    SHA-256withECDSA 会先计算哈希;NONEwithECDSA 对摘要签名,该签名针对原消息验证通过。

    检查记录
    fact ecdsa-prehash = hashes-message-or-digest
  • 格式错误的 ECDSA 签名

    returns-false

    传给 SHA-256withECDSA 的原始 r‖s 签名和 3 字节的数据块都返回 false。

    检查记录
    fact ecdsa-malformed-signature = returns-false
    evidence ecdsa-malformed-signature: raw r||s to SHA-256withECDSA: false; 3 bytes: false
  • ECDSA 曲线

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-224、P-256、P-384、P-521、secp256k1 和 brainpoolP256r1/P384r1/P512r1 的签名与验证均成功。

    检查记录
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 和 Ed448。

    检查记录
    fact eddsa = Ed25519, Ed448
  • 后量子算法

    ML-KEMML-DSASLH-DSA

    在托管代码中,ML-KEM-768 的封装与解封装、ML-DSA-65 和 SLH-DSA-SHA2-128f 的签名与验证均成功。

    检查记录
    fact pqc = ML-KEM, ML-DSA, SLH-DSA
  • 公钥格式

    spki-pemspki-der

    通过 PemReader 读取 SPKI PEM,通过 PublicKeyFactory 读取 SPKI DER。

    检查记录
    fact public-key-formats = spki-pem, spki-der
    evidence public-key-formats: PublicKeyFactory.CreateKey(SPKI DER) verifies: True
  • 私钥格式

    pkcs8-dersec1-pem

    PemWriter 写出、PemReader 读取 SEC1 PEM;PKCS#8 DER 通过 PrivateKeyFactory 加载。

    检查记录
    fact private-key-formats = pkcs8-der, sec1-pem
    evidence private-key-formats: PemWriter writes -----BEGIN EC PRIVATE KEY-----; PemReader returns AsymmetricCipherKeyPair; PKCS#8 DER round trip signs: True

用法

用 System.Security.Cryptography 不提供的 Ed25519 签名并验证。
using System.Text;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

var message = Encoding.UTF8.GetBytes("order #1042 approved");

// Ed25519, which the .NET base class library does not offer.
var generator = new Ed25519KeyPairGenerator();
generator.Init(new Ed25519KeyGenerationParameters(new SecureRandom()));
var pair = generator.GenerateKeyPair();

var signer = SignerUtilities.GetSigner("Ed25519");
signer.Init(true, pair.Private);
signer.BlockUpdate(message, 0, message.Length);
var signature = signer.GenerateSignature();

var verifier = SignerUtilities.GetSigner("Ed25519");
verifier.Init(false, pair.Public);
verifier.BlockUpdate(message, 0, message.Length);
Console.WriteLine($"signature: {signature.Length} bytes, verified: {verifier.VerifySignature(signature)}");

代码片段测试工具会在容器镜像 mcr.microsoft.com/dotnet/sdk:10.0.402-noble 中原样运行每个示例,只要输出不匹配,运行就会失败。

环境准备
dotnet new console --force -o . >/dev/null && dotnet add package BouncyCastle.Cryptography --version 2.7.0 >/dev/null
命令
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
输出必须匹配(正则表达式)
signature: 64 bytes, verified: True

库