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

.NET System.Security.Cryptography

System.Security.Cryptography в .NET: ECDSA по умолчанию подписывает в формате raw r‖s (IEEE P1363), в отличие от всех стеков на основе OpenSSL; встроенного Ed25519 нет, а доступность постквантовых классов зависит от операционной системы.
Лицензия
MIT
Проверенная версия
10.0.13 ·
Исходный код
github.com/dotnet/runtime
Документация
learn.microsoft.com/dotnet/api/system.security.cryptography.ecdsa

Совместимость вкратце

ECDSA

Кривые ECDSA
P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
none
Форматы закрытых ключей
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalar

.NET скрывает криптографию платформы — CNG в Windows, OpenSSL в Linux, библиотеки Apple в macOS — за единым API: ECDsa, RSA, MLDsa, MLKem и другими классами. Проверка выполнялась на .NET 10.0.13 в Ubuntu 24.04, где под ним работает OpenSSL 3.0.13, и эта версия определяет ряд результатов.

Кодировка подписи по умолчанию противоположна той, что принята в большинстве серверных библиотек. ECDsa.SignData возвращает 64-байтовую подпись IEEE P1363 r‖s, и VerifyData ожидает именно её; для DER в обоих вызовах нужен DSASignatureFormat.Rfc3279DerSequence. Поэтому .NET без дополнительных настроек совместим с JWS и WebCrypto, но не с OpenSSL, SHA256withECDSA в Java и SignASN1 в Go. На любую некорректную или неподходящую подпись VerifyData возвращает false, поэтому подпись в DER, проверенная с форматом по умолчанию, выглядит точно так же, как подделка; пример показывает оба исхода, а конвертер DER ⇄ raw показывает, какая форма у вас.

В Linux ECDSA работал на кривых от P-256 до P-521, secp256k1 и Brainpool r1, во всех случаях через OpenSSL. SignHash подписывает вычисленный вами дайджест, nonce случайные, s не нормализуется, а проверка принимает high-S. В библиотеке базовых классов нет классов для Ed25519 и Ed448; для EdDSA нужен Bouncy Castle для .NET или другой пакет.

MLKem, MLDsa, SlhDsa и CompositeMLDsa есть в .NET 10, но значение IsSupported у каждого из них зависит от платформы. С OpenSSL 3.0.13 у всех было false; ML-KEM и ML-DSA появились в OpenSSL 3.5, поэтому в Linux для них нужен дистрибутив с этой версией, а в Windows — соответствующая поддержка в CNG. Код должен проверять IsSupported, а не рассчитывать на поддержку заранее; ни одной платформы, где ответ равен true, эта страница не проверяла.

Ключи импортируются из SPKI и PKCS#8 (PEM или DER, с шифрованием или без), SEC1 EC PRIVATE KEY и координат в формате raw в ECParameters; импорта из точки SEC1 или JWK нет. Разобрать файлы PFX поможет инспектор PFX.

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

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/dotnet-crypto/run.sh запустила .NET System.Security.Cryptography 10.0.13 в среде .NET 10.0.13 on Ubuntu 24.04.5 LTS в образе контейнера mcr.microsoft.com/dotnet/sdk:10.0.402-noble; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

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

    raw

    По умолчанию SignData возвращает 64 байта IEEE P1363 r‖s; с DSASignatureFormat.Rfc3279DerSequence получается DER.

    Что записала проверка
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: SignData default: 64 bytes; with Rfc3279DerSequence: parses as DER True
  • Nonce в ECDSA

    random

    Случайный nonce: две подписи одного и того же сообщения ключом из RFC 6979 различались.

    Что записала проверка
    fact ecdsa-nonce = random
  • Low-S при подписи

    either

    s не нормализуется: среди 64 подписей встретились значения high-S.

    Что записала проверка
    fact ecdsa-low-s-sign = either
  • High-S при проверке

    accepts-high-s

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

    Что записала проверка
    fact ecdsa-low-s-verify = accepts-high-s
  • Что хеширует вызов подписи

    hashes-message-or-digest

    SignData хеширует данные; SignHash подписывает вычисленный вами дайджест, и такая подпись прошла проверку по сообщению.

    Что записала проверка
    fact ecdsa-prehash = hashes-message-or-digest
  • Некорректная подпись ECDSA

    returns-false

    Во всех случаях возвращается false: подпись в DER, проверенная как P1363, подпись raw, проверенная как DER, и 3-байтовый фрагмент данных.

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

    P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-256, P-384, P-521, secp256k1 и brainpoolP256r1/P384r1/P512r1: в Linux подпись и проверка прошли успешно.

    Что записала проверка
    fact ecdsa-curves = P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    none

    В System.Security.Cryptography нет класса Ed25519.

    Что записала проверка
    fact eddsa = none
    evidence eddsa: System.Security.Cryptography.Ed25519 type present: False
  • Постквантовые алгоритмы

    none

    На этой платформе MLKem.IsSupported, MLDsa.IsSupported, SlhDsa.IsSupported и признак поддержки составного ML-DSA были равны false.

    Что записала проверка
    fact pqc = none
    evidence pqc: MLKem.IsSupported False, MLDsa.IsSupported False, SlhDsa.IsSupported False, CompositeMLDsa.IsSupported(MLDsa65WithECDsaP256) False
  • Системный OpenSSL

    3.0.13

    В Linux классы используют системный OpenSSL, здесь это версия 3.0.13 из Ubuntu 24.04, в которой ещё нет ML-KEM и ML-DSA.

    Что записала проверка
    fact platform-openssl = 3.0.13
    evidence platform-openssl: OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
  • Форматы открытых ключей

    spki-pemspki-derraw-xy

    Открытые ключи импортируются из SPKI PEM (ImportFromPem), SPKI DER и из координат X и Y в формате raw в ECParameters.

    Что записала проверка
    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
  • Форматы закрытых ключей

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

    Закрытые ключи импортируются из PKCS#8 PEM и DER, зашифрованного PKCS#8, SEC1 PEM и DER, а также из скаляра в формате raw в ECParameters.

    Что записала проверка
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der, raw-scalar

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

Подпишите в формате P1363 по умолчанию и в DER и убедитесь, что для проверки DER нужен соответствующий аргумент формата.
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]);

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера mcr.microsoft.com/dotnet/sdk:10.0.402-noble, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
dotnet new console --force -o . >/dev/null
Команда
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
Вывод должен совпадать (регулярное выражение)
raw: 64 bytes, DER verified: True, DER as raw: False\n-----BEGIN PUBLIC KEY-----

Библиотеки