Aller au contenu
ecdsa.com
.NET

.NET System.Security.Cryptography

System.Security.Cryptography de .NET : un ECDSA qui signe par défaut en r‖s brut (IEEE P1363), contrairement à toutes les piles fondées sur OpenSSL, pas d’Ed25519 intégré, et des classes post-quantiques dont la disponibilité dépend du système d’exploitation.
Licence
MIT
Version vérifiée
10.0.13 ·
Code source
github.com/dotnet/runtime
Documentation
learn.microsoft.com/dotnet/api/system.security.cryptography.ecdsa

Compatibilité en un coup d’œil

ECDSA

Courbes ECDSA
P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
none
Formats de clé publique
spki-pemspki-derraw-xy
Formats de clé privée
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalar

.NET enveloppe la cryptographie de la plateforme (CNG sous Windows, OpenSSL sous Linux, les bibliothèques d’Apple sous macOS) derrière une seule API : ECDsa, RSA, MLDsa, MLKem et consorts. Les essais ont été menés avec .NET 10.0.13 sous Ubuntu 24.04, où l’OpenSSL sous-jacent est la version 3.0.13, et cette version détermine plusieurs résultats.

L’encodage de signature par défaut est l’inverse de celui de la plupart des bibliothèques côté serveur. ECDsa.SignData renvoie le r‖s IEEE P1363 de 64 octets et VerifyData l’attend ; le DER exige DSASignatureFormat.Rfc3279DerSequence dans les deux appels. .NET est ainsi compatible d’emblée avec JWS et WebCrypto, mais pas avec OpenSSL, SHA256withECDSA de Java ni SignASN1 de Go. Toute signature mal formée ou au mauvais format fait renvoyer false à VerifyData, si bien qu’une signature DER contrôlée avec le format par défaut ressemble exactement à une falsification : l’exemple montre les deux issues, et le convertisseur DER ⇄ brut indique laquelle des deux formes vous avez.

Sous Linux, ECDSA a couvert de P-256 à P-521, secp256k1 et les courbes Brainpool r1, toutes via OpenSSL. SignHash signe un condensat que vous avez calculé, les nonces sont aléatoires, s n’est pas normalisé et la vérification accepte high-S. La bibliothèque de classes de base ne contient aucune classe Ed25519 ni Ed448 ; EdDSA exige Bouncy Castle pour .NET ou un autre paquet.

MLKem, MLDsa, SlhDsa et CompositeMLDsa existent dans .NET 10, mais chacune indique IsSupported selon la plateforme. Avec OpenSSL 3.0.13, toutes valaient false ; OpenSSL a ajouté ML-KEM et ML-DSA dans la 3.5, si bien que sous Linux elles exigent une distribution qui fournit cette version, et sous Windows la prise en charge CNG correspondante. Le code doit vérifier IsSupported au lieu de le supposer, et cette page n’a vérifié aucune plateforme où la réponse est true.

Les clés s’importent depuis SPKI et PKCS#8 (PEM ou DER, chiffré ou non), SEC1 EC PRIVATE KEY et les coordonnées brutes d’ECParameters ; il n’existe pas d’import de point SEC1 ni de JWK. Inspectez les fichiers PFX avec l’inspecteur PFX.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/dotnet-crypto/run.sh a exécuté .NET System.Security.Cryptography 10.0.13 sur .NET 10.0.13 on Ubuntu 24.04.5 LTS dans l’image de conteneur mcr.microsoft.com/dotnet/sdk:10.0.402-noble le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Encodage des signatures ECDSA

    raw

    SignData renvoie par défaut 64 octets de r‖s IEEE P1363 ; DSASignatureFormat.Rfc3279DerSequence produit du DER.

    Relevé par la vérification
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: SignData default: 64 bytes; with Rfc3279DerSequence: parses as DER True
  • Nonce ECDSA

    random

    Nonce aléatoire : la clé de test de la RFC 6979 a signé deux fois le même message avec des résultats différents.

    Relevé par la vérification
    fact ecdsa-nonce = random
  • Low-S à la signature

    either

    s n’est pas normalisé : des valeurs high-S sont apparues parmi 64 signatures.

    Relevé par la vérification
    fact ecdsa-low-s-sign = either
  • High-S à la vérification

    accepts-high-s

    Une signature dont s a été remplacé par n − s a été vérifiée.

    Relevé par la vérification
    fact ecdsa-low-s-verify = accepts-high-s
  • Ce que l’appel de signature hache

    hashes-message-or-digest

    SignData hache les données ; SignHash signe un condensat calculé par vos soins, et cette signature a été vérifiée sur le message.

    Relevé par la vérification
    fact ecdsa-prehash = hashes-message-or-digest
  • Signature ECDSA mal formée

    returns-false

    Une signature DER contrôlée comme P1363, une signature brute contrôlée comme DER et un bloc de 3 octets renvoient tous false.

    Relevé par la vérification
    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
  • Courbes ECDSA

    P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    Signature et vérification réussies sous Linux avec P-256, P-384, P-521, secp256k1 et brainpoolP256r1/P384r1/P512r1.

    Relevé par la vérification
    fact ecdsa-curves = P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    none

    Il n’y a pas de classe Ed25519 dans System.Security.Cryptography.

    Relevé par la vérification
    fact eddsa = none
    evidence eddsa: System.Security.Cryptography.Ed25519 type present: False
  • Algorithmes post-quantiques

    none

    MLKem.IsSupported, MLDsa.IsSupported, SlhDsa.IsSupported et la prise en charge du ML-DSA composite valaient tous false sur cette plateforme.

    Relevé par la vérification
    fact pqc = none
    evidence pqc: MLKem.IsSupported False, MLDsa.IsSupported False, SlhDsa.IsSupported False, CompositeMLDsa.IsSupported(MLDsa65WithECDsaP256) False
  • OpenSSL de la plateforme

    3.0.13

    Sous Linux, les classes utilisent l’OpenSSL du système, ici la 3.0.13 d’Ubuntu 24.04, antérieure à ML-KEM et ML-DSA.

    Relevé par la vérification
    fact platform-openssl = 3.0.13
    evidence platform-openssl: OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
  • Formats de clé publique

    spki-pemspki-derraw-xy

    Les clés publiques s’importent depuis SPKI PEM (ImportFromPem), SPKI DER et les coordonnées X et Y brutes dans ECParameters.

    Relevé par la vérification
    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
  • Formats de clé privée

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

    Les clés privées s’importent depuis PKCS#8 PEM et DER, PKCS#8 chiffré, SEC1 PEM et DER, et le scalaire brut dans ECParameters.

    Relevé par la vérification
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der, raw-scalar

Utilisation

Signer au format P1363 par défaut et en DER, et montrer que la vérification du DER exige l’argument de format correspondant.
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]);

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur mcr.microsoft.com/dotnet/sdk:10.0.402-noble, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
dotnet new console --force -o . >/dev/null
Commande
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
La sortie doit correspondre à cette expression régulière
raw: 64 bytes, DER verified: True, DER as raw: False\n-----BEGIN PUBLIC KEY-----

Bibliothèques