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

pyca/cryptography

Пакет cryptography от Python Cryptographic Authority — стандартная криптографическая библиотека для Python: ECDSA на базе OpenSSL с подписями DER и RFC 6979 по запросу, EdDSA, ML-KEM и ML-DSA, но без поддержки JWK.
Лицензия
Apache-2.0 OR BSD-3-Clause
Проверенная версия
50.0.2 ·
Исходный код
github.com/pyca/cryptography
Документация
cryptography.io/en/latest/

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

ECDSAEdDSAПостквантовая криптография

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

Большая часть кода на Python — PyJWT, Authlib, paramiko, certbot, SDK от AWS и Google — выполняет реальные криптографические операции через cryptography. Слой hazmat даёт прямой доступ к ключам и подписям; wheel-пакеты содержат собственную сборку OpenSSL, поэтому поведение зависит от версии пакета, а не от операционной системы (в 50.0.2 входит OpenSSL 4.0.3).

Подписи ECDSA создаются в ASN.1 DER, и verify() ожидает DER. WebCrypto, JWS и большинство аппаратных токенов используют 64-байтовый формат raw r‖s, который verify() отклоняет с тем же исключением InvalidSignature, что и поддельную подпись. decode_dss_signature и encode_dss_signature преобразуют DER в пару целых чисел и обратно; дополнение до фиксированной длины нужно добавить самостоятельно, как показано в примере, а проверить результат вручную можно в конвертере DER ⇄ raw.

Nonce выбирается случайно, если не запросить RFC 6979: вызов ec.ECDSA(hashes.SHA256(), deterministic_signing=True) точно воспроизвёл тестовый вектор из RFC для кривой P-256 — вплоть до значения s в форме high-S, потому что пакет нигде не нормализует s, а проверка принимает обе половины диапазона. Подписи, которые будет проверять код с правилом low-S, например libsecp256k1 или @noble/curves, сначала нужно преобразовать, заменив s на n − s. sign() и verify() сами хешируют данные; если у вас уже есть дайджест, передайте Prehashed(hashes.SHA256()), иначе данные будут хешированы дважды.

В версии 50.0.2 поддерживается широкий набор кривых (от P-224 до P-521, secp256k1 и кривые Brainpool r1), а также Ed25519, Ed448, ML-KEM-768/1024 и ML-DSA-44/65/87. Ключи загружаются из PEM и DER (SPKI, PKCS#8, зашифрованный PKCS#8, SEC1), OpenSSH и из точек SEC1 без обёртки; чтения JWK нет, поэтому ключи JWK приходится обрабатывать через PyJWT, Authlib или jwcrypto либо через конвертер JWK.

Большинство ошибок, которые ищут в поисковиках, возникают при загрузке ключей, а не при подписи: открытый ключ, переданный в load_pem_private_key, зашифрованный ключ без пароля или точка, которая не лежит на указанной кривой. Если подпись из другого стека не проходит проверку, помогут инструмент проверки подписей и матрица совместимости библиотек.

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

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

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

    der

    sign() возвращает ASN.1 DER, а verify() ожидает DER.

    Что записала проверка
    fact ecdsa-signature-encoding = der
  • 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
  • Nonce в ECDSA

    rfc6979-opt-in

    По умолчанию случайный; с deterministic_signing=True подпись точно совпала с приложением A.2.5 RFC 6979, включая значение s в форме high-S.

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

    hashes-message-or-digest

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

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

    throws

    Подпись raw r‖s, 3-байтовый фрагмент данных и подпись другого сообщения одинаково вызывают InvalidSignature; по исключению не понять, какой из случаев произошёл.

    Что записала проверка
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: raw r||s: InvalidSignature
    evidence ecdsa-malformed-signature: 3 bytes: InvalidSignature
    evidence ecdsa-malformed-signature: wrong message: InvalidSignature
  • Кривые 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
  • Постквантовые алгоритмы

    ML-KEMML-DSA

    ML-KEM-768 и ML-KEM-1024 прошли инкапсуляцию и декапсуляцию, а для ML-DSA-44/65/87 подпись и проверка прошли успешно; класса для ML-KEM-512 нет, как нет и SLH-DSA.

    Что записала проверка
    fact pqc = ML-KEM, ML-DSA
    evidence pqc: ML-KEM-512 class present: False; SLH-DSA module present: False
  • Форматы открытых ключей

    spki-pemspki-dersec1-uncompressedsec1-compressedopenssh

    Открытые ключи загружаются из SPKI в PEM и DER, OpenSSH и точек SEC1 (сжатых или несжатых) через from_encoded_point; 64-байтовое значение x‖y без префикса отклонено, а поддержки JWK нет.

    Что записала проверка
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, openssh
    evidence public-key-formats: no JWK reader or writer in the package
  • Форматы закрытых ключей

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalaropenssh

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

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

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

Подпишите ключом P-256, преобразуйте подпись DER в raw r‖s и обратно и проверьте её.
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature

key = ec.generate_private_key(ec.SECP256R1())
message = b"order #1042 approved"

# DER out of sign(); turn it into the 64-byte r||s that WebCrypto and JOSE expect.
der = key.sign(message, ec.ECDSA(hashes.SHA256()))
r, s = decode_dss_signature(der)
raw = r.to_bytes(32, "big") + s.to_bytes(32, "big")

# A raw signature from WebCrypto goes the other way before verify().
back = encode_dss_signature(int.from_bytes(raw[:32], "big"), int.from_bytes(raw[32:], "big"))
try:
    key.public_key().verify(back, message, ec.ECDSA(hashes.SHA256()))
    print("verified: True, raw bytes:", len(raw))
except InvalidSignature:
    print("verified: False")

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

Подготовка
pip install --quiet --root-user-action=ignore cryptography==50.0.2
Команда
python usage.py
Вывод должен совпадать (регулярное выражение)
verified: True, raw bytes: 64

Инструменты

Термины

Библиотеки

Статьи

Рецепты

Ошибки

Библиотеки