Pular para o conteúdo
ecdsa.com
.NET

.NET System.Security.Cryptography

O System.Security.Cryptography do .NET: ECDSA que assina em r‖s bruto (IEEE P1363) por padrão, ao contrário de todas as stacks baseadas no OpenSSL, sem Ed25519 nativo e com classes pós-quânticas cuja disponibilidade depende do sistema operacional.
Licença
MIT
Versão verificada
10.0.13 ·
Código-fonte
github.com/dotnet/runtime
Documentação
learn.microsoft.com/dotnet/api/system.security.cryptography.ecdsa

Visão geral da compatibilidade

ECDSA

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

O .NET encapsula a criptografia da plataforma — CNG no Windows, OpenSSL no Linux, as bibliotecas da Apple no macOS — por trás de uma única API: ECDsa, RSA, MLDsa, MLKem e companhia. O teste rodou o .NET 10.0.13 no Ubuntu 24.04, em que o OpenSSL por baixo é o 3.0.13, e essa versão determina vários dos resultados.

A codificação de assinatura padrão é o oposto da usada pela maioria das bibliotecas do lado do servidor. ECDsa.SignData retorna o r‖s IEEE P1363 de 64 bytes e VerifyData espera esse formato; o DER exige DSASignatureFormat.Rfc3279DerSequence nas duas chamadas. Com isso, o .NET coincide, sem configuração extra, com o JWS e com a WebCrypto e diverge do OpenSSL, do SHA256withECDSA do Java e do SignASN1 do Go. Toda assinatura malformada ou incompatível faz VerifyData retornar false, então uma assinatura DER verificada com o formato padrão parece exatamente uma falsificação — o exemplo mostra os dois resultados, e o conversor DER ⇄ bruto mostra qual das duas formas você tem.

No Linux, o ECDSA cobriu da P-256 à P-521, a secp256k1 e as curvas Brainpool r1, tudo por meio do OpenSSL. SignHash assina um digest calculado por você, os nonces são aleatórios, o s não é normalizado e a verificação aceita high-S. Não há classe Ed25519 nem Ed448 na biblioteca de classes base; para EdDSA, é preciso o Bouncy Castle para .NET ou outro pacote.

MLKem, MLDsa, SlhDsa e CompositeMLDsa existem no .NET 10, mas cada uma informa IsSupported de acordo com a plataforma. Com o OpenSSL 3.0.13, o valor foi false em todas; o OpenSSL adicionou ML-KEM e ML-DSA na versão 3.5, então, no Linux, elas precisam de uma distribuição que traga essa versão e, no Windows, do suporte correspondente no CNG. O código deve verificar IsSupported em vez de presumir, e esta página não verificou nenhuma plataforma em que a resposta seja true.

As chaves são importadas de SPKI e PKCS#8 (PEM ou DER, criptografados ou não), de SEC1 EC PRIVATE KEY e de coordenadas brutas em ECParameters; não há importação de ponto SEC1 nem de JWK. Inspecione pacotes PFX com o inspetor de PFX.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/dotnet-crypto/run.sh executou .NET System.Security.Cryptography 10.0.13 em .NET 10.0.13 on Ubuntu 24.04.5 LTS na imagem de contêiner mcr.microsoft.com/dotnet/sdk:10.0.402-noble em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Codificação da assinatura ECDSA

    raw

    SignData retorna por padrão 64 bytes de r‖s IEEE P1363; DSASignatureFormat.Rfc3279DerSequence produz DER.

    Registrado pela verificação
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: SignData default: 64 bytes; with Rfc3279DerSequence: parses as DER True
  • Nonce do ECDSA

    random

    Nonce aleatório: a chave da RFC 6979 assinou a mesma mensagem duas vezes com resultados diferentes.

    Registrado pela verificação
    fact ecdsa-nonce = random
  • Low-S ao assinar

    either

    O s não é normalizado: valores high-S apareceram entre 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
  • High-S ao verificar

    accepts-high-s

    Uma assinatura com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    SignData calcula o hash; SignHash assina um digest calculado por você, e essa assinatura foi verificada contra a mensagem.

    Registrado pela verificação
    fact ecdsa-prehash = hashes-message-or-digest
  • Assinatura ECDSA malformada

    returns-false

    Uma assinatura DER verificada como P1363, uma assinatura bruta verificada como DER e um blob de 3 bytes retornam todos false.

    Registrado pela verificação
    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

    Assinatura e verificação funcionaram no Linux com P-256, P-384, P-521, secp256k1 e brainpoolP256r1/P384r1/P512r1.

    Registrado pela verificação
    fact ecdsa-curves = P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    none

    Não há classe Ed25519 em System.Security.Cryptography.

    Registrado pela verificação
    fact eddsa = none
    evidence eddsa: System.Security.Cryptography.Ed25519 type present: False
  • Algoritmos pós-quânticos

    none

    MLKem.IsSupported, MLDsa.IsSupported, SlhDsa.IsSupported e o suporte a ML-DSA composto foram todos false nesta plataforma.

    Registrado pela verificação
    fact pqc = none
    evidence pqc: MLKem.IsSupported False, MLDsa.IsSupported False, SlhDsa.IsSupported False, CompositeMLDsa.IsSupported(MLDsa65WithECDsaP256) False
  • OpenSSL da plataforma

    3.0.13

    No Linux, as classes usam o OpenSSL do sistema, aqui o 3.0.13 do Ubuntu 24.04, que é anterior ao ML-KEM e ao ML-DSA.

    Registrado pela verificação
    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 chave pública

    spki-pemspki-derraw-xy

    Chaves públicas são importadas de SPKI PEM (ImportFromPem), de SPKI DER e das coordenadas brutas X e Y em ECParameters.

    Registrado pela verificação
    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 chave privada

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

    Chaves privadas são importadas de PKCS#8 PEM e DER, de PKCS#8 criptografado, de SEC1 PEM e DER e do escalar bruto em ECParameters.

    Registrado pela verificação
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der, raw-scalar

Uso

Assine no formato padrão P1363 e em DER e veja que verificar DER exige o argumento de formato correspondente.
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]);

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner mcr.microsoft.com/dotnet/sdk:10.0.402-noble, e a execução falha se a saída não corresponder.

Configuração
dotnet new console --force -o . >/dev/null
Comando
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
A saída deve corresponder a (expressão regular)
raw: 64 bytes, DER verified: True, DER as raw: False\n-----BEGIN PUBLIC KEY-----

Bibliotecas