Zum Inhalt springen
ecdsa.com
Python

python-ecdsa

Die ECDSA- und EdDSA-Bibliothek von tlsfuzzer in reinem Python: rohe r‖s-Signaturen über SHA-1, sofern nicht anders angegeben, standardmäßig zufällige Nonces, RFC 6979 nur einen Methodenaufruf entfernt, und öffentliche Schlüssel als bloßes x‖y.
Lizenz
MIT
Geprüfte Version
0.19.2 ·
Quellcode
github.com/tlsfuzzer/python-ecdsa
Dokumentation
ecdsa.readthedocs.io/en/latest/

Kompatibilität auf einen Blick

ECDSAEdDSA

ECDSA-Kurven
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Formate öffentlicher Schlüssel
spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy
Formate privater Schlüssel
pkcs8-pemsec1-pemsec1-derraw-scalar

ecdsa implementiert ECDSA und EdDSA in reinem Python, ohne OpenSSL darunter. Dadurch lässt es sich überall leicht installieren und eignet sich gut für Testvektoren und die Lehre; außerdem ist es noch immer eine Abhängigkeit älterer JOSE-Stacks wie python-jose. Laut den Maintainern ist die Bibliothek nicht dafür ausgelegt, Seitenkanalangriffen zu widerstehen, deshalb gehört das Signieren in Produktivsystemen in pyca/cryptography oder coincurve.

Drei Standardeinstellungen verursachen fast jeden Interoperabilitätsfehler. Der Hash ist standardmäßig SHA-1: Ein Objekt SigningKey, das ohne hashfunc erzeugt wurde, signiert SHA-1-Digests, was die Prüfung bestätigte – mit pyca/cryptography geprüft, bestand die Standardsignatur mit SHA-1 und scheiterte mit SHA-256. Die Signatur ist rohes r‖s (64 Byte bei P-256), nicht DER. Und VerifyingKey.to_string() liefert das bloße 64 Byte lange x‖y ohne Präfix 0x04, das OpenSSL, Go und WebCrypto nicht als Punkt lesen.

Beim Signieren wird eine zufällige Nonce verwendet, sofern du nicht sign_deterministic() aufrufst; diese Methode reproduzierte den P-256-Testvektor aus RFC 6979. s bleibt so, wie es berechnet wurde – in etwa der Hälfte der Fälle hoch –, sofern du keinen der Encoder *_canonize wählst, und die Prüfung akzeptiert beide Hälften. Jede Wahl der Kodierung wird pro Aufruf über sigencode= und sigdecode= getroffen, daher müssen signierende und prüfende Seite dasselbe Paar angeben; eine DER-Signatur, die an den standardmäßigen Decoder für rohe Signaturen übergeben wird, löst BadSignatureError: Malformed formatting of signature aus.

Die Kurvenunterstützung ist die breiteste unter den Python-Bibliotheken – P-224 bis P-521, secp256k1 und Brainpool –, dazu kommen Ed25519 und Ed448. Schlüssel werden als SPKI-PEM oder -DER, SEC1-Punkte und rohe Skalare eingelesen; to_pem() schreibt SEC1 EC PRIVATE KEY, sofern du nicht format="pkcs8" übergibst.

Das Beispiel korrigiert alle drei Standardeinstellungen auf einmal: SHA-256, eine deterministische Nonce und eine kanonische Low-S-DER-Signatur. Um eine damit erzeugte Signatur gegen eine andere Bibliothek zu prüfen, sind der Konverter DER ⇄ Raw und die Testvektoren der schnellste Weg.

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/python-ecdsa/run.sh hat python-ecdsa 0.19.2 am 20. Oktober 2026 im Container-Image python:3.14.8-slim-trixie ausgeführt; die Seite muss mit seiner Ausgabe übereinstimmen, und jeder Fakt führt die zugehörigen Zeilen auf.

  • Kodierung von ECDSA-Signaturen

    raw

    sign() liefert bei P-256 das 64 Byte lange rohe r‖s; sigencode_der erzeugt DER, und die Prüfung braucht dann sigdecode_der.

    Von der Prüfung aufgezeichnet
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: sigencode_der output parses as DER: True; verifies with sigdecode_der: verified
  • Standard-Hash

    sha1

    Ohne hashfunc signieren Schlüssel SHA-1-Digests: Die Standardsignatur bestand die Prüfung in pyca/cryptography mit SHA-1, nicht aber mit SHA-256.

    Von der Prüfung aufgezeichnet
    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 beim Signieren

    either

    Die einfachen Encoder lassen s unverändert, wie es berechnet wurde (unter 64 Signaturen trat High-S auf); sigencode_string_canonize und sigencode_der_canonize normalisieren es.

    Von der Prüfung aufgezeichnet
    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 bei der Prüfung

    accepts-high-s

    Eine Signatur, in der s durch n − s ersetzt war, bestand die Prüfung.

    Von der Prüfung aufgezeichnet
    fact ecdsa-low-s-verify = accepts-high-s
  • ECDSA-Nonce

    rfc6979-opt-in

    sign() verwendet eine zufällige Nonce; sign_deterministic() reproduzierte Anhang A.2.5 von RFC 6979.

    Von der Prüfung aufgezeichnet
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: sign() repeats: False; sign_deterministic() reproduces RFC 6979 A.2.5: True
  • Was der Signaturaufruf hasht

    hashes-message-or-digest

    sign() hasht die Nachricht; sign_digest() signiert einen selbst berechneten Digest, und diese Signatur bestand die Prüfung gegen die Nachricht.

    Von der Prüfung aufgezeichnet
    fact ecdsa-prehash = hashes-message-or-digest
  • Fehlerhafte ECDSA-Signatur

    throws

    Eine Eingabe mit falscher Länge löst BadSignatureError: Malformed formatting of signature aus; eine gültige Kodierung über die falsche Nachricht löst BadSignatureError: Signature verification failed aus.

    Von der Prüfung aufgezeichnet
    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-Kurven

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

    P-224, P-256, P-384, P-521, secp256k1 und die Brainpool-r1-Kurven.

    Von der Prüfung aufgezeichnet
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 und Ed448.

    Von der Prüfung aufgezeichnet
    fact eddsa = Ed25519, Ed448
  • Post-Quanten-Algorithmen

    none

    Keine Post-Quanten-Algorithmen.

    Von der Prüfung aufgezeichnet
    fact pqc = none
  • Standardformat öffentlicher Schlüssel

    raw-xy

    VerifyingKey.to_string() liefert 64 Byte x‖y ohne das Präfix 0x04; übergib "uncompressed" oder "compressed" für SEC1-Kodierungen.

    Von der Prüfung aufgezeichnet
    fact default-public-key = raw-xy
  • Formate öffentlicher Schlüssel

    spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy

    Öffentliche Schlüssel wurden aus SPKI-PEM und -DER, SEC1-Punkten und dem bloßen x‖y geladen.

    Von der Prüfung aufgezeichnet
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, raw-xy
  • Formate privater Schlüssel

    pkcs8-pemsec1-pemsec1-derraw-scalar

    Private Schlüssel wurden aus PKCS#8-PEM, SEC1-PEM und -DER sowie rohen Skalaren geladen; to_pem() schreibt standardmäßig -----BEGIN EC PRIVATE KEY-----.

    Von der Prüfung aufgezeichnet
    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-----

Verwendung

Signiere mit SHA-256, einer Nonce nach RFC 6979 und einer kanonischen Low-S-DER-Kodierung und prüfe die Signatur anschließend.
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])

Die Snippet-Testumgebung führt jedes Beispiel unverändert im Container-Image python:3.14.8-slim-trixie aus, und der Lauf schlägt fehl, wenn die Ausgabe nicht übereinstimmt.

Einrichtung
pip install --quiet --root-user-action=ignore ecdsa==0.19.2
Befehl
python usage.py
Ausgabe muss passen (regulärer Ausdruck)
deterministic: True\nverified: True\n-----BEGIN PUBLIC KEY-----

Bibliotheken