Saltar al contenido
ecdsa.com
.NET

Bouncy Castle for .NET (BouncyCastle.Cryptography)

Bouncy Castle para .NET, la biblioteca criptográfica administrada que los desarrolladores de .NET añaden para cubrir lo que le falta a la biblioteca de clases base: Ed25519 y Ed448, secp256k1 en todas las plataformas, ECDSA determinista, PGP, PEM, y ML-KEM, ML-DSA y SLH-DSA sin depender del sistema operativo.
Licencia
MIT
Versión comprobada
2.7.0 ·
Código fuente
github.com/bcgit/bc-csharp
Documentación
www.bouncycastle.org/documentation/documentation-c/

Compatibilidad de un vistazo

ECDSAEdDSACriptografía poscuántica

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Algoritmos poscuánticos
ML-KEMML-DSASLH-DSA
Formatos de clave privada
pkcs8-dersec1-pem

BouncyCastle.Cryptography (el paquete que sustituyó a Portable.BouncyCastle) es código administrado puro: sus resultados no dependen de Windows CNG ni de la versión de OpenSSL de Linux, y eso es justo lo que lo hace útil junto a System.Security.Cryptography. Ofrece una API SignerUtilities basada en cadenas y clases de más bajo nivel como ECDsaSigner.

Sus valores predeterminados de ECDSA siguen la tradición de OpenSSL y no la de .NET: SHA-256withECDSA produce DER, mientras que SHA-256withPLAIN-ECDSA da el r‖s en bruto de 64 bytes que usan el propio ECDsa de .NET y JWS. Confundirlos es el fallo de interoperabilidad habitual entre las dos bibliotecas dentro de una misma aplicación, y cualquier entrada mal formada devuelve false en lugar de lanzar una excepción. Los nonces son aleatorios con SignerUtilities; ECDsaSigner con un HMacDsaKCalculator reprodujo el vector de prueba del RFC 6979. s no se normaliza y la verificación acepta high-S.

La cobertura es el motivo por el que se añade: de P-224 a P-521, secp256k1 y las curvas Brainpool r1; Ed25519 y Ed448, como en el ejemplo; y ML-KEM, ML-DSA y SLH-DSA mediante MLKemEncapsulator, MLDsaSigner y SlhDsaSigner, que funcionaron todos en la misma imagen de Linux en la que la biblioteca de clases base no indicaba ninguna compatibilidad poscuántica. Prueba los algoritmos en la herramienta de firmas poscuánticas.

PemWriter escribe las claves privadas EC como SEC1 -----BEGIN EC PRIVATE KEY----- y PemReader las devuelve como un AsymmetricCipherKeyPair; PKCS#8 pasa por PrivateKeyInfoFactory. La versión propia del ensamblado es la de nombre seguro 2.0.0 en todas las versiones 2.x, así que comprueba la versión del paquete (aquí, 2.7.0) cuando compares comportamientos.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/bouncy-castle-csharp/run.sh ejecutó Bouncy Castle for .NET (BouncyCastle.Cryptography) 2.7.0 en la imagen de contenedor mcr.microsoft.com/dotnet/sdk:10.0.402-noble el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Codificación de la firma ECDSA

    der

    SHA-256withECDSA devuelve DER; SHA-256withPLAIN-ECDSA devuelve 64 bytes de r‖s en bruto.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = der
    evidence ecdsa-signature-encoding: SHA-256withPLAIN-ECDSA output: 64 bytes
  • Nonce de ECDSA

    rfc6979-opt-in

    Aleatorio con SignerUtilities; ECDsaSigner con HMacDsaKCalculator reprodujo el apéndice A.2.5 del RFC 6979.

    Registrado por la comprobación
    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 al firmar

    either

    s no se normaliza: aparecieron valores high-S entre 64 firmas.

    Registrado por la comprobación
    fact ecdsa-low-s-sign = either
  • High-S al verificar

    accepts-high-s

    Se verificó una firma DER con s sustituido por n − s.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • De qué calcula el hash la llamada de firma

    hashes-message-or-digest

    SHA-256withECDSA calcula el hash; NONEwithECDSA firma un resumen, y esa firma se verificó contra el mensaje.

    Registrado por la comprobación
    fact ecdsa-prehash = hashes-message-or-digest
  • Firma ECDSA mal formada

    returns-false

    Una firma r‖s en bruto pasada a SHA-256withECDSA y un blob de 3 bytes devuelven false.

    Registrado por la comprobación
    fact ecdsa-malformed-signature = returns-false
    evidence ecdsa-malformed-signature: raw r||s to SHA-256withECDSA: false; 3 bytes: false
  • Curvas ECDSA

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

    Con P-224, P-256, P-384, P-521, secp256k1 y brainpoolP256r1/P384r1/P512r1 se firmó y se verificó.

    Registrado por la comprobación
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 y Ed448.

    Registrado por la comprobación
    fact eddsa = Ed25519, Ed448
  • Algoritmos poscuánticos

    ML-KEMML-DSASLH-DSA

    Con ML-KEM-768 se encapsuló y desencapsuló, y con ML-DSA-65 y SLH-DSA-SHA2-128f se firmó y verificó, en código administrado.

    Registrado por la comprobación
    fact pqc = ML-KEM, ML-DSA, SLH-DSA
  • Formatos de clave pública

    spki-pemspki-der

    SPKI PEM mediante PemReader y SPKI DER mediante PublicKeyFactory.

    Registrado por la comprobación
    fact public-key-formats = spki-pem, spki-der
    evidence public-key-formats: PublicKeyFactory.CreateKey(SPKI DER) verifies: True
  • Formatos de clave privada

    pkcs8-dersec1-pem

    PemWriter escribe y PemReader lee SEC1 PEM; PKCS#8 DER se carga mediante PrivateKeyFactory.

    Registrado por la comprobación
    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

Uso

Firma y verifica con Ed25519, que System.Security.Cryptography no ofrece.
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)}");

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor mcr.microsoft.com/dotnet/sdk:10.0.402-noble, y la ejecución falla si la salida no coincide.

Configuración
dotnet new console --force -o . >/dev/null && dotnet add package BouncyCastle.Cryptography --version 2.7.0 >/dev/null
Comando
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
La salida debe coincidir (expresión regular)
signature: 64 bytes, verified: True

Bibliotecas