Перейти к содержимому
ecdsa.com
.NET

Bouncy Castle for .NET (BouncyCastle.Cryptography)

Bouncy Castle для .NET — управляемая криптографическая библиотека, которую .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

BouncyCastle.Cryptography (пакет, пришедший на смену Portable.BouncyCastle) — полностью управляемый код: его результаты не зависят ни от Windows CNG, ни от версии OpenSSL в Linux, и именно этим он полезен рядом с System.Security.Cryptography. Он предлагает API SignerUtilities, в котором алгоритмы задаются строками, и низкоуровневые классы, например ECDsaSigner.

Параметры ECDSA по умолчанию следуют традиции OpenSSL, а не .NET: SHA-256withECDSA выдаёт DER, а SHA-256withPLAIN-ECDSA — 64-байтовую подпись raw r‖s, которую используют собственный класс ECDsa в .NET и JWS. Путаница между ними — типичный сбой совместимости, когда обе библиотеки работают в одном приложении, причём любые некорректные входные данные приводят к возврату false, а не к исключению. Через SignerUtilities nonce выбираются случайно; ECDsaSigner с HMacDsaKCalculator воспроизвёл тестовый вектор RFC 6979. s не нормализуется, а проверка принимает high-S.

Bouncy Castle подключают ради широты поддержки: кривые от P-224 до P-521, secp256k1 и Brainpool r1; Ed25519 и Ed448, как в примере; а также ML-KEM, ML-DSA и SLH-DSA через MLKemEncapsulator, MLDsaSigner и SlhDsaSigner — и всё это работало на том же образе Linux, где библиотека базовых классов не сообщила вообще ни о какой поддержке постквантовых алгоритмов. Попробуйте эти алгоритмы в песочнице постквантовых подписей.

PemWriter записывает закрытые ключи EC в формате SEC1 -----BEGIN EC PRIVATE KEY-----, а PemReader возвращает их как AsymmetricCipherKeyPair; PKCS#8 обрабатывается через PrivateKeyInfoFactory. Версия самой сборки в строгом имени — 2.0.0 для каждого релиза 2.x, поэтому при сравнении поведения сверяйте версию пакета (здесь 2.7.0).

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/bouncy-castle-csharp/run.sh запустила Bouncy Castle for .NET (BouncyCastle.Cryptography) 2.7.0 в образе контейнера mcr.microsoft.com/dotnet/sdk:10.0.402-noble; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Кодировка подписи ECDSA

    der

    SHA-256withECDSA возвращает DER; SHA-256withPLAIN-ECDSA возвращает 64 байта в формате raw r‖s.

    Что записала проверка
    fact ecdsa-signature-encoding = der
    evidence ecdsa-signature-encoding: SHA-256withPLAIN-ECDSA output: 64 bytes
  • Nonce в ECDSA

    rfc6979-opt-in

    Случайный при работе через SignerUtilities; ECDsaSigner с HMacDsaKCalculator воспроизвёл вектор из приложения A.2.5 RFC 6979.

    Что записала проверка
    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

    Подпись в DER, в которой s заменили на n − s, прошла проверку.

    Что записала проверка
    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

    Подпись raw r‖s, переданная в SHA-256withECDSA, и 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

    SPKI PEM через PemReader и SPKI DER через PublicKeyFactory.

    Что записала проверка
    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

Использование

Подпишите и проверьте подпись с Ed25519, которого нет в System.Security.Cryptography.
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

Библиотеки