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

python-ecdsa

Библиотека ECDSA и EdDSA на чистом Python от tlsfuzzer: подписи raw r‖s по SHA-1, если не указано иное, случайные nonce по умолчанию (RFC 6979 — через отдельный метод) и открытые ключи в виде x‖y без префикса.
Лицензия
MIT
Проверенная версия
0.19.2 ·
Исходный код
github.com/tlsfuzzer/python-ecdsa
Документация
ecdsa.readthedocs.io/en/latest/

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

ECDSAEdDSA

Кривые ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Форматы открытых ключей
spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy
Форматы закрытых ключей
pkcs8-pemsec1-pemsec1-derraw-scalar

Библиотека ecdsa реализует ECDSA и EdDSA на чистом Python, без OpenSSL. Поэтому её легко установить где угодно, она удобна для тестовых векторов и обучения и до сих пор остаётся зависимостью старых стеков JOSE, например python-jose. Сопровождающие прямо пишут, что библиотека не рассчитана на защиту от атак по побочным каналам, поэтому в рабочих системах подписи следует создавать с помощью pyca/cryptography или coincurve.

Почти все сбои совместимости вызваны тремя значениями по умолчанию. Хеш-функция по умолчанию — SHA-1: SigningKey, созданный без hashfunc, подписывает дайджесты SHA-1, и это подтвердилось при проверке подписи с настройками по умолчанию в pyca/cryptography: с SHA-1 она прошла, а с SHA-256 — нет. Подпись имеет формат raw r‖s (64 байта на P-256), а не DER. Наконец, VerifyingKey.to_string() возвращает только 64 байта x‖y без префикса 0x04, а OpenSSL, Go и WebCrypto не принимают такие данные как точку.

При подписи используется случайный nonce, если не вызвать sign_deterministic(); этот метод воспроизвёл тестовый вектор P-256 из RFC 6979. Значение s остаётся таким, каким получилось при вычислении (примерно в половине случаев это high-S), если не выбрать один из кодировщиков *_canonize, а проверка принимает s из любой половины диапазона. Кодировка выбирается при каждом вызове через sigencode= и sigdecode=, поэтому сторона подписи и сторона проверки должны указать одну и ту же пару; подпись DER, переданная декодеру raw по умолчанию, вызывает BadSignatureError: Malformed formatting of signature.

Среди библиотек для Python у неё самый широкий набор кривых (от P-224 до P-521, secp256k1 и Brainpool), а также Ed25519 и Ed448. Ключи принимаются в виде SPKI в PEM или DER, точек SEC1 и скаляров в формате raw; to_pem() записывает SEC1 EC PRIVATE KEY, если не передать format="pkcs8".

Пример сразу меняет все три значения по умолчанию: SHA-256, детерминированный nonce и каноническая подпись DER с low-S. Чтобы сверить созданную им подпись с другой библиотекой, быстрее всего воспользоваться конвертером DER ⇄ raw и генератором тестовых векторов.

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

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/python-ecdsa/run.sh запустила python-ecdsa 0.19.2 в образе контейнера python:3.14.8-slim-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

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

    raw

    sign() возвращает 64-байтовую подпись raw r‖s на P-256; sigencode_der даёт DER, и тогда для проверки нужен sigdecode_der.

    Что записала проверка
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: sigencode_der output parses as DER: True; verifies with sigdecode_der: verified
  • Хеш-функция по умолчанию

    sha1

    Без hashfunc ключи подписывают дайджесты SHA-1: подпись с настройками по умолчанию прошла проверку в pyca/cryptography с SHA-1, но не с SHA-256.

    Что записала проверка
    fact ecdsa-default-hash = sha1
    evidence ecdsa-default-hash: checked by verifying the default signature in pyca/cryptography with SHA-1 and with SHA-256
  • Low-S при подписи

    either

    Обычные кодировщики оставляют s как есть (среди 64 подписей встретились значения high-S); sigencode_string_canonize и sigencode_der_canonize его нормализуют.

    Что записала проверка
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: high-S among 64 plain signatures: True; among 64 with sigencode_string_canonize: False
    evidence ecdsa-low-s-sign: sigencode_strings_canonize exists: True
  • High-S при проверке

    accepts-high-s

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

    Что записала проверка
    fact ecdsa-low-s-verify = accepts-high-s
  • Nonce в ECDSA

    rfc6979-opt-in

    sign() использует случайный nonce; sign_deterministic() воспроизвёл результат из приложения A.2.5 RFC 6979.

    Что записала проверка
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: sign() repeats: False; sign_deterministic() reproduces RFC 6979 A.2.5: True
  • Что хеширует вызов подписи

    hashes-message-or-digest

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

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

    throws

    Входные данные неверной длины вызывают BadSignatureError: Malformed formatting of signature; подпись в корректной кодировке, но для другого сообщения вызывает BadSignatureError: Signature verification failed.

    Что записала проверка
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: DER where raw is expected: BadSignatureError: Malformed formatting of signature
    evidence ecdsa-malformed-signature: 3 bytes: BadSignatureError: Malformed formatting of signature
    evidence ecdsa-malformed-signature: wrong message: BadSignatureError: Signature verification failed
  • Кривые ECDSA

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

    P-224, P-256, P-384, P-521, secp256k1 и кривые Brainpool r1.

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

    Ed25519Ed448

    Ed25519 и Ed448.

    Что записала проверка
    fact eddsa = Ed25519, Ed448
  • Постквантовые алгоритмы

    none

    Постквантовых алгоритмов нет.

    Что записала проверка
    fact pqc = none
  • Формат открытого ключа по умолчанию

    raw-xy

    VerifyingKey.to_string() возвращает 64 байта x‖y без префикса 0x04; для кодировок SEC1 передайте "uncompressed" или "compressed".

    Что записала проверка
    fact default-public-key = raw-xy
  • Форматы открытых ключей

    spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy

    Открытые ключи загружаются из SPKI в PEM и DER, точек SEC1 и значения x‖y без префикса.

    Что записала проверка
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, raw-xy
  • Форматы закрытых ключей

    pkcs8-pemsec1-pemsec1-derraw-scalar

    Закрытые ключи загружаются из PKCS#8 в PEM, SEC1 в PEM и DER и из скаляров в формате raw; to_pem() по умолчанию записывает -----BEGIN EC PRIVATE KEY-----.

    Что записала проверка
    fact private-key-formats = pkcs8-pem, sec1-pem, sec1-der, raw-scalar
    evidence private-key-formats: to_pem() without format writes -----BEGIN EC PRIVATE KEY-----

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

Подпишите с SHA-256, nonce по RFC 6979 и канонической кодировкой DER с low-S, затем проверьте подпись.
import hashlib

from ecdsa import NIST256p, SigningKey
from ecdsa.util import sigdecode_der, sigencode_der_canonize

key = SigningKey.generate(curve=NIST256p, hashfunc=hashlib.sha256)  # the default would be SHA-1
public = key.get_verifying_key()
message = b"order #1042 approved"

# RFC 6979 nonce, DER encoding, s normalized to the low half: what OpenSSL-style
# verifiers read and what strict low-S verifiers accept.
signature = key.sign_deterministic(message, sigencode=sigencode_der_canonize)
again = key.sign_deterministic(message, sigencode=sigencode_der_canonize)

print("deterministic:", signature == again)
print("verified:", public.verify(signature, message, sigdecode=sigdecode_der))
print(public.to_pem().decode().splitlines()[0])

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера python:3.14.8-slim-trixie, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
pip install --quiet --root-user-action=ignore ecdsa==0.19.2
Команда
python usage.py
Вывод должен совпадать (регулярное выражение)
deterministic: True\nverified: True\n-----BEGIN PUBLIC KEY-----

Библиотеки