Saltar al contenido
ecdsa.com
.NET

.NET System.Security.Cryptography

System.Security.Cryptography de .NET: ECDSA que firma por defecto en r‖s en bruto (IEEE P1363), a diferencia de todas las pilas basadas en OpenSSL, sin Ed25519 de serie y con clases poscuánticas cuya disponibilidad depende del sistema operativo.
Licencia
MIT
Versión comprobada
10.0.13 ·
Código fuente
github.com/dotnet/runtime
Documentación
learn.microsoft.com/dotnet/api/system.security.cryptography.ecdsa

Compatibilidad de un vistazo

ECDSA

Curvas ECDSA
P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
none
Formatos de clave pública
spki-pemspki-derraw-xy
Formatos de clave privada
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalar

.NET envuelve la criptografía de la plataforma —CNG en Windows, OpenSSL en Linux, las bibliotecas de Apple en macOS— tras una única API: ECDsa, RSA, MLDsa, MLKem y compañía. La comprobación ejecutó .NET 10.0.13 en Ubuntu 24.04, donde el OpenSSL subyacente es el 3.0.13, y esa versión condiciona varios resultados.

La codificación de firma predeterminada es la opuesta a la de la mayoría de las bibliotecas del lado del servidor. ECDsa.SignData devuelve el r‖s IEEE P1363 de 64 bytes y VerifyData lo espera; DER requiere DSASignatureFormat.Rfc3279DerSequence en ambas llamadas. Así, .NET coincide de serie con JWS y WebCrypto, pero no con OpenSSL, ni con SHA256withECDSA de Java ni con SignASN1 de Go. Cualquier firma mal formada o que no corresponda hace que VerifyData devuelva false, así que una firma DER comprobada con el formato predeterminado parece exactamente una falsificación: el ejemplo muestra ambos resultados, y el conversor DER ⇄ en bruto muestra cuál de las dos formas tienes.

En Linux, ECDSA abarcó de P-256 a P-521, secp256k1 y las curvas Brainpool r1, todo a través de OpenSSL. SignHash firma un resumen que calculas tú, los nonces son aleatorios, s no se normaliza y la verificación acepta high-S. No hay ninguna clase Ed25519 ni Ed448 en la biblioteca de clases base; para EdDSA hace falta Bouncy Castle para .NET u otro paquete.

MLKem, MLDsa, SlhDsa y CompositeMLDsa existen en .NET 10, pero cada una informa IsSupported según la plataforma. Con OpenSSL 3.0.13 todas daban false; OpenSSL añadió ML-KEM y ML-DSA en la 3.5, así que en Linux necesitan una distribución que la incluya, y en Windows, la compatibilidad correspondiente de CNG. El código debe comprobar IsSupported en lugar de darlo por hecho, y esta página no ha verificado ninguna plataforma en la que la respuesta sea true.

Las claves se importan desde SPKI y PKCS#8 (PEM o DER, cifrados o no), SEC1 EC PRIVATE KEY y coordenadas en bruto de ECParameters; no hay importación de puntos SEC1 ni de JWK. Inspecciona archivos PFX con el inspector de PFX.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/dotnet-crypto/run.sh ejecutó .NET System.Security.Cryptography 10.0.13 en .NET 10.0.13 on Ubuntu 24.04.5 LTS 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

    raw

    SignData devuelve por defecto 64 bytes de r‖s IEEE P1363; DSASignatureFormat.Rfc3279DerSequence produce DER.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: SignData default: 64 bytes; with Rfc3279DerSequence: parses as DER True
  • Nonce de ECDSA

    random

    Nonce aleatorio: la clave del RFC 6979 firmó el mismo mensaje dos veces con resultados distintos.

    Registrado por la comprobación
    fact ecdsa-nonce = random
  • 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 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

    SignData calcula el hash; SignHash firma un resumen que calculas tú, 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 DER comprobada como P1363, una firma en bruto comprobada como DER y un blob de 3 bytes devuelven false.

    Registrado por la comprobación
    fact ecdsa-malformed-signature = returns-false
    evidence ecdsa-malformed-signature: DER to the default verifier: false; raw to the DER verifier: false; 3 bytes: false
  • Curvas ECDSA

    P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

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

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

    none

    No hay ninguna clase Ed25519 en System.Security.Cryptography.

    Registrado por la comprobación
    fact eddsa = none
    evidence eddsa: System.Security.Cryptography.Ed25519 type present: False
  • Algoritmos poscuánticos

    none

    MLKem.IsSupported, MLDsa.IsSupported, SlhDsa.IsSupported y la compatibilidad con ML-DSA compuesto dieron false en esta plataforma.

    Registrado por la comprobación
    fact pqc = none
    evidence pqc: MLKem.IsSupported False, MLDsa.IsSupported False, SlhDsa.IsSupported False, CompositeMLDsa.IsSupported(MLDsa65WithECDsaP256) False
  • OpenSSL de la plataforma

    3.0.13

    En Linux, las clases usan el OpenSSL del sistema, aquí el 3.0.13 de Ubuntu 24.04, anterior a ML-KEM y ML-DSA.

    Registrado por la comprobación
    fact platform-openssl = 3.0.13
    evidence platform-openssl: OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
  • Formatos de clave pública

    spki-pemspki-derraw-xy

    Las claves públicas se importan desde SPKI PEM (ImportFromPem), SPKI DER y las coordenadas X e Y en bruto de ECParameters.

    Registrado por la comprobación
    fact public-key-formats = spki-pem, spki-der, raw-xy
    evidence public-key-formats: ImportParameters takes Q.X and Q.Y separately; there is no SEC1 point or JWK import in the BCL
  • Formatos de clave privada

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalar

    Las claves privadas se importan desde PKCS#8 PEM y DER, PKCS#8 cifrado, SEC1 PEM y DER, y el escalar en bruto de ECParameters.

    Registrado por la comprobación
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der, raw-scalar

Uso

Firma en el formato P1363 predeterminado y en DER, y muestra que verificar DER requiere el argumento de formato correspondiente.
using System.Security.Cryptography;
using System.Text;

using var key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var message = Encoding.UTF8.GetBytes("order #1042 approved");

// .NET signs raw r||s (IEEE P1363) by default; OpenSSL, Java and Go expect DER.
var raw = key.SignData(message, HashAlgorithmName.SHA256);
var der = key.SignData(message, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence);

// Verifying a DER signature needs the same format argument, or VerifyData returns false.
var ok = key.VerifyData(message, der, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence);
var wrongFormat = key.VerifyData(message, der, HashAlgorithmName.SHA256);
Console.WriteLine($"raw: {raw.Length} bytes, DER verified: {ok}, DER as raw: {wrongFormat}");
Console.WriteLine(key.ExportSubjectPublicKeyInfoPem().Split('\n')[0]);

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
Comando
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
La salida debe coincidir (expresión regular)
raw: 64 bytes, DER verified: True, DER as raw: False\n-----BEGIN PUBLIC KEY-----

Bibliotecas