
ECDSA-Signatur/-Verifizierung und ECDH-Schlüsselaustausch in reinem Python
Dies ist eine einfach zu verwendende Implementierung von ECC (Elliptic Curve Cryptography, Elliptische-Kurven-Kryptografie) mit Unterstützung für ECDSA (Elliptic Curve Digital Signature Algorithm, Elliptische-Kurven-Digitalsignaturalgorithmus), EdDSA (Edwards-curve Digital Signature Algorithm, Edwards-Kurven-Digitalsignaturalgorithmus) und ECDH (Elliptic Curve Diffie-Hellman, Elliptische-Kurven-Diffie-Hellman), vollständig in Python implementiert und unter der MIT-Lizenz veröffentlicht. Mit dieser Bibliothek können Sie schnell Schlüsselpaare (Signaturschlüssel und Verifikationsschlüssel) erstellen, Nachrichten signieren und die Signaturen verifizieren. Sie können auch einen gemeinsamen geheimen Schlüssel auf der Grundlage ausgetauschter öffentlicher Schlüssel vereinbaren. Die Schlüssel und Signaturen sind sehr kurz, wodurch sie einfach zu handhaben und in andere Protokolle einzubinden sind.
HINWEIS: Diese Bibliothek sollte nicht in Produktionsumgebungen verwendet werden; weitere Einzelheiten finden Sie unter Sicherheit.
Diese Bibliothek bietet Schlüsselerzeugung, Signieren, Verifizieren und Ableitung gemeinsamer Geheimnisse
für fünf
beliebte NIST-„Suite-B“-GF(p)-Kurven (Primkörper), mit Schlüssellängen von 192,
224, 256, 384 und 521 Bits. Die „Kurznamen“ dieser Kurven, wie sie vom
OpenSSL-Tool (openssl ecparam -list_curves) bekannt sind, lauten: prime192v1,
secp224r1, prime256v1, secp384r1 und secp521r1. Enthalten ist auch die
256-Bit-Kurve secp256k1, die von Bitcoin verwendet wird. Es gibt auch Unterstützung für die
regulären (nicht getwisteten) Varianten der Brainpool-Kurven von 160 bis 512 Bits. Die
„Kurznamen“ dieser Kurven lauten: brainpoolP160r1, brainpoolP192r1,
brainpoolP224r1, brainpoolP256r1, brainpoolP320r1, brainpoolP384r1,
. Einige der kleinen Kurven aus dem SEC-Standard sind ebenfalls
enthalten (hauptsächlich um das Testen der Bibliothek zu beschleunigen), das sind:
, , und .
Schlüsselerzeugung, Signieren und Verifizieren wird auch für die Kurven Ed25519 und
Ed448 unterstützt.
Weitere Kurven sind nicht enthalten, aber es ist nicht allzu schwer, Unterstützung für weitere
Kurven über Primkörpern hinzuzufügen.
Diese Bibliothek verwendet nur Python und das Paket „six“. Sie ist kompatibel mit Python 2.6, 2.7 und 3.6+. Sie unterstützt auch die Ausführung auf alternativen Implementierungen wie pypy und pypy3.
Wenn gmpy2 oder gmpy installiert ist, werden diese für schnellere Arithmetik verwendet.
Eines von beiden kann nach der Installation dieser Bibliothek installiert werden;
python-ecdsa erkennt deren Vorhandensein beim Start und verwendet sie
automatisch.
Für optimale Leistung sollten Sie unter Python3 gmpy2 bevorzugen.
Um die OpenSSL-Kompatibilitätstests auszuführen, muss sich das Tool „openssl“ in Ihrem
PATH befinden. Diese Version wurde erfolgreich gegen OpenSSL 0.9.8o,
1.0.0a, 1.0.2f, 1.1.1d und 3.0.1 getestet (unter anderem).
Diese Bibliothek ist auf PyPI verfügbar; es wird empfohlen, sie mit pip zu installieren:```
pip install ecdsa
Falls eine höhere Leistung gewünscht ist und die Verwendung von nativem Code kein Problem darstellt,
ist es möglich, die Installation zusammen mit `gmpy2` anzugeben:```
pip install ecdsa[gmpy2]
oder (langsamere, Legacy-Option):``` pip install ecdsa[gmpy]
## Speed
Die folgende Tabelle zeigt, wie lange diese Bibliothek benötigt, um Schlüsselpaare zu erzeugen (`keygen`), Daten zu signieren (`sign`), diese Signaturen zu verifizieren (`verify`), ein gemeinsames Geheimnis abzuleiten (`ecdh`) und die Signaturen ohne schlüsselspezifische Vorabberechnung zu verifizieren (`no PC verify`). Alle Werte sind in Sekunden angegeben. Der Übersichtlichkeit halber sind auch die Kehrwerte dieser Werte aufgeführt: wie viele Schlüssel pro Sekunde erzeugt werden können (`keygen/s`), wie viele Signaturen pro Sekunde erstellt werden können (`sign/s`), wie viele Signaturen pro Sekunde verifiziert werden können (`verify/s`), wie viele gemeinsame Geheimnisse pro Sekunde abgeleitet werden können (`ecdh/s`) und wie viele Signaturen ohne schlüsselspezifische Vorabberechnung pro Sekunde verifiziert werden können (`no PC verify/s`). Die Größe der rohen Signatur (im Allgemeinen die kleinste Art, wie eine Signatur kodiert werden kann) ist ebenfalls in der Spalte `siglen` angegeben. Verwenden Sie `tox -e speed`, um diese Tabelle auf Ihrem eigenen Computer zu erzeugen. Auf einem Intel Core i7 4790K @ 4.0GHz erziele ich die folgende Leistung:```
siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s
NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43
NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66
NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27
NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00
NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39
SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01
BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92
BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29
BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16
BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25
BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49
BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20
BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61
SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20
SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16
SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16
SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07
Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50
Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69
ecdh ecdh/s
NIST192p: 0.00104s 964.89
NIST224p: 0.00134s 748.63
NIST256p: 0.00170s 587.08
NIST384p: 0.00352s 283.90
NIST521p: 0.00717s 139.51
SECP256k1: 0.00154s 648.40
BRAINPOOLP160r1: 0.00082s 1220.70
BRAINPOOLP192r1: 0.00105s 956.75
BRAINPOOLP224r1: 0.00136s 734.52
BRAINPOOLP256r1: 0.00178s 563.32
BRAINPOOLP320r1: 0.00252s 397.23
BRAINPOOLP384r1: 0.00376s 266.27
BRAINPOOLP512r1: 0.00733s 136.35
SECP112r1: 0.00046s 2180.40
SECP112r2: 0.00045s 2229.14
SECP128r1: 0.00054s 1868.15
SECP160r1: 0.00080s 1243.98
Um die Leistung zu testen, wenn gmpy2 geladen ist, verwende tox -e speedgmpy2.
Auf derselben Maschine erhalte ich mit gmpy2 die folgende Leistung:```
siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s
NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41
NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90
NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82
NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43
NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40
SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32
BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81
BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19
BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87
BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60
BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34
BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85
BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83
SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83
SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28
SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88
SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35
Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02
Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62
ecdh ecdh/s
NIST192p: 0.00050s 1985.70
NIST224p: 0.00066s 1524.16
NIST256p: 0.00071s 1413.07
NIST384p: 0.00127s 788.89
NIST521p: 0.00230s 434.85
SECP256k1: 0.00071s 1409.95
BRAINPOOLP160r1: 0.00042s 2374.65 BRAINPOOLP192r1: 0.00051s 1960.01 BRAINPOOLP224r1: 0.00066s 1518.37 BRAINPOOLP256r1: 0.00071s 1399.90 BRAINPOOLP320r1: 0.00100s 997.21 BRAINPOOLP384r1: 0.00129s 777.51 BRAINPOOLP512r1: 0.00210s 475.99 SECP112r1: 0.00022s 4457.70 SECP112r2: 0.00024s 4252.33 SECP128r1: 0.00028s 3589.31 SECP160r1: 0.00043s 2305.02
(dort gibt es auch die `gmpy`-Version, führen Sie sie mit `tox -e speedgmpy` aus)
Zum Vergleich: Eine hochoptimierte Implementierung (inklusive kurvenspezifischer
Assembly für einige Kurven), wie die in OpenSSL 1.1.1d, liefert auf derselben
Maschine die folgenden Leistungswerte.
Führen Sie `openssl speed ecdsa` und `openssl speed ecdh` aus, um sie zu reproduzieren:```
sign verify sign/s verify/s
192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7
224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0
256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6
384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1
521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5
256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2
384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1
512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1
sign verify sign/s verify/s
253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7
456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7
op op/s
192 bits ecdh (nistp192) 0.0002s 4853.4
224 bits ecdh (nistp224) 0.0001s 15252.1
256 bits ecdh (nistp256) 0.0001s 18436.3
384 bits ecdh (nistp384) 0.0008s 1292.7
521 bits ecdh (nistp521) 0.0003s 2884.7
256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5
384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0
512 bits ecdh (brainpoolP512r1) 0.0014s 694.8
Schlüssel und Signaturen können auf verschiedene Weise serialisiert werden (siehe Verwendung unten). Für einen NIST192p-Schlüssel benötigen die drei grundlegenden Darstellungen Zeichenketten der folgenden Längen (in Bytes):
to_string: signkey= 24, verifykey= 48, signature=48
compressed: signkey=n/a, verifykey= 25, signature=n/a
DER: signkey=106, verifykey= 80, signature=55
PEM: signkey=278, verifykey=162, (no support for PEM signatures)
Im Jahr 2006 kündigte Peter Pearson seine reine Python-Implementierung von ECDSA in einer Nachricht an sci.crypt an, die von seiner Download-Seite verfügbar ist. Im Jahr 2010 schrieb Brian Warner einen Wrapper um diesen Code, um die Verwendung etwas einfacher und sicherer zu machen. Im Jahr 2020 integrierte Hubert Kario eine Implementierung der Elliptische-Kurven-Kryptografie, die intern Jacobi-Koordinaten verwendet, was die Leistung um etwa das 20-Fache verbessert. Sie sehen sich gerade die README für diesen Wrapper an.
Um die vollständige Testsuite auszuführen, gehen Sie wie folgt vor:
tox -e coverage
Auf einem Intel Core i7 4790K @ 4,0 GHz dauert die Ausführung der Tests etwa 18 Sekunden. Die Testsuite verwendet hypothesis, daher gibt es eine gewisse inhärente Schwankung in der Ausführungszeit der Testsuite.
Ein Teil von test_pyecdsa.py und test_ecdh.py prüft die Kompatibilität mit OpenSSL, indem das „openssl“-CLI-Tool ausgeführt wird. Stellen Sie sicher, dass es sich in Ihrem PATH befindet, wenn Sie die Kompatibilität damit testen möchten (wenn OpenSSL fehlt, zu alt ist oder nicht alle Kurven unterstützt, die in den Upstream-Releases unterstützt werden, werden Sie im obigen coverage-Lauf übersprungene Tests sehen).
Diese Bibliothek wurde nicht mit Blick auf Sicherheit entworfen. Wenn Sie Daten verarbeiten, die geschützt werden müssen, empfehlen wir Ihnen, einen qualitativ hochwertigen Wrapper um OpenSSL zu verwenden. pyca/cryptography ist ein Beispiel für einen solchen Wrapper. Der primäre Verwendungszweck dieser Bibliothek ist die Verwendung als portable Bibliothek für Interoperabilitätstests und als Lehrmittel.
Diese Bibliothek bietet keinen Schutz gegen Seitenkanalangriffe.
Erlauben Sie Angreifern nicht, zu messen, wie lange Sie für die Erzeugung eines Schlüsselpaars oder die Signierung einer Nachricht benötigen. Erlauben Sie Angreifern nicht, Code auf derselben physischen Maschine auszuführen, während die Schlüsselpaarerzeugung oder Signierung stattfindet (dies schließt virtuelle Maschinen ein). Erlauben Sie Angreifern nicht, zu messen, wie viel Strom Ihr Computer während der Erzeugung des Schlüsselpaars oder der Signierung einer Nachricht verbraucht. Erlauben Sie Angreifern nicht, HF-Störungen zu messen, die von Ihrem Computer während der Erzeugung eines Schlüsselpaars oder der Signierung einer Nachricht ausgehen. Hinweis: Bereits das Laden des privaten Schlüssels führt zur Schlüsselpaarerzeugung. Andere Operationen oder Angriffsvektoren können ebenfalls anfällig für Angriffe sein. Für einen ausgeklügelten Angreifer reicht die Beobachtung nur einer Operation mit einem privaten Schlüssel aus, um den privaten Schlüssel vollständig zu rekonstruieren.
Bitte beachten Sie außerdem, dass jede reine Python-Kryptografiebibliothek anfällig für dieselben Seitenkanalangriffe sein wird. Dies liegt daran, dass Python keine seitenkanalsicheren Primitive bereitstellt (mit Ausnahme von hmac.compare_digest()), was seitenkanalsichere Programmierung unmöglich macht.
Diese Bibliothek ist auf eine starke Quelle von Zufallszahlen angewiesen. Verwenden Sie sie nicht auf einem System, auf dem os.urandom() keine kryptografisch sicheren Zufallszahlen liefert.
Sie beginnen mit der Erstellung eines SigningKey. Sie können damit Daten signieren, indem Sie die Daten als Byte-String übergeben und die Signatur (ebenfalls ein Byte-String) zurückerhalten. Sie können einen SigningKey auch bitten, Ihnen den entsprechenden VerifyingKey zu liefern. Der VerifyingKey kann verwendet werden, um eine Signatur zu verifizieren, indem Sie ihm sowohl die Datenzeichenkette als auch den Signatur-Byte-String übergeben: Er gibt entweder True zurück oder wirft BadSignatureError.```python
from ecdsa import SigningKey
sk = SigningKey.generate() # uses NIST192p
vk = sk.verifying_key
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
Jedes `SigningKey`/`VerifyingKey` ist mit einer bestimmten Kurve verknüpft, wie zum Beispiel
NIST192p (der Standardkurve). Längere Kurven sind sicherer, benötigen aber mehr Zeit
bei der Verwendung und ergeben längere Schlüssel und Signaturen.```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.verifying_key
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
Der SigningKey kann in mehrere verschiedene Formate serialisiert werden: Die kürzeste
ist, s=sk.to_string() aufzurufen und ihn dann mit
SigningKey.from_string(s, curve) neu zu erstellen. Diese Kurzform erfasst die
Kurve nicht, also musst du sicherstellen, dass du an from_string() dieselbe Kurve übergibst, die du
für den ursprünglichen Schlüssel verwendet hast. Die Kurzform eines NIST192p-basierten Signaturschlüssels ist nur 24
Bytes lang. Wenn eine Punktkodierung ungültig ist oder nicht auf der angegebenen
Kurve liegt, löst from_string() einen MalformedPointError aus.```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
sk_string = sk.to_string()
sk2 = SigningKey.from_string(sk_string, curve=NIST384p)
print(sk_string.hex())
print(sk2.to_string().hex())
Hinweis: Obwohl die Methoden `to_string()` heißen, ist der Typ, den sie zurückgeben,
tatsächlich `bytes`; der Teil „string“ ist ein Überbleibsel aus Python 2.
`sk.to_pem()` und `sk.to_der()` serialisieren den Signaturschlüssel in dieselben
Formate, die OpenSSL verwendet. Die PEM-Datei sieht aus wie das bekannte ASCII-umhüllte
`"-----BEGIN EC PRIVATE KEY-----"`-Format in Base64-Kodierung, und das DER-Format
ist eine kürzere binäre Form derselben Daten.
`SigningKey.from_pem()/.from_der()` machen diese Serialisierung rückgängig. Diese
Formate enthalten den Kurvennamen, daher müssen Sie keinen Kurven-
bezeichner an den Deserialisierer übergeben. Falls die Datei fehlerhaft ist, werden `from_der()`
und `from_pem()` `UnexpectedDER` oder` MalformedPointError` auslösen.```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
sk_pem = sk.to_pem()
sk2 = SigningKey.from_pem(sk_pem)
# sk and sk2 are the same key
Ebenso kann der VerifyingKey auf dieselbe Weise serialisiert werden:
vk.to_string()/VerifyingKey.from_string(), to_pem()/from_pem() und
to_der()/from_der(). Dasselbe curve=-Argument wird für
VerifyingKey.from_string() benötigt.```python
from ecdsa import SigningKey, VerifyingKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.verifying_key
vk_string = vk.to_string()
vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p)
from ecdsa import SigningKey, VerifyingKey, NIST384p sk = SigningKey.generate(curve=NIST384p) vk = sk.verifying_key vk_pem = vk.to_pem() vk2 = VerifyingKey.from_pem(vk_pem)
Es gibt ein paar verschiedene Möglichkeiten, eine Signatur zu berechnen. Grundsätzlich
nimmt ECDSA eine Zahl, die die zu signierenden Daten repräsentiert, und gibt ein
Paar von Zahlen zurück, die die Signatur darstellen. Das Argument `hashfunc=` von
`sk.sign()` und `vk.verify()` wird verwendet, um einen beliebigen String in einen
Digest fester Länge umzuwandeln, der dann in eine Zahl umgewandelt wird, die ECDSA
signieren kann, und sowohl Signieren als auch Verifizieren müssen denselben Ansatz
verwenden. Der Standardwert ist
`hashlib.sha1`, aber wenn Sie NIST256p oder eine längere Kurve verwenden, können Sie
stattdessen `hashlib.sha256` verwenden.
Es gibt auch mehrere Möglichkeiten, eine Signatur darzustellen. Die Standardmethoden
`sk.sign()` und `vk.verify()` präsentieren sie als kurzen String, aus
Einfachheit und für minimalen Overhead. Um ein anderes Schema zu verwenden, nutzen Sie
die Argumente `sk.sign(sigencode=)` und `vk.verify(sigdecode=)`. Es gibt Hilfsfunktionen
im Modul `ecdsa.util`, die hier nützlich sein können.
Es ist auch möglich, einen `SigningKey` aus einem „Seed" zu erstellen, der
deterministisch ist. Dies kann in Protokollen verwendet werden, in denen Sie
konsistente Signaturschlüssel aus einem anderen Geheimnis ableiten möchten, zum Beispiel wenn Sie
drei separate Schlüssel möchten und nur ein einziges Master-Geheimnis speichern wollen. Sie sollten
mit einem gleichverteilten, unerratbaren Seed mit etwa `curve.baselen`
Bytes Entropie beginnen und dann eine der Hilfsfunktionen in `ecdsa.util` verwenden, um
ihn in eine Ganzzahl im korrekten Bereich umzuwandeln, und ihn schließlich
an `SigningKey.from_secret_exponent()` übergeben, wie folgt:```python
import os
from ecdsa import NIST384p, SigningKey
from ecdsa.util import randrange_from_seed__trytryagain
def make_key(seed):
secexp = randrange_from_seed__trytryagain(seed, NIST384p.order)
return SigningKey.from_secret_exponent(secexp, curve=NIST384p)
seed = os.urandom(NIST384p.baselen) # or other starting point
sk1a = make_key(seed)
sk1b = make_key(seed)
# note: sk1a and sk1b are the same key
assert sk1a.to_string() == sk1b.to_string()
sk2 = make_key(b"2-"+seed) # different key
assert sk1a.to_string() != sk2.to_string()
Falls die Anwendung viele Signaturen überprüfen wird, die mit einem einzigen Schlüssel erstellt wurden, ist es möglich, einige der internen Werte vorzuberechnen, um die Signaturprüfung erheblich zu beschleunigen. Der Break-Even-Punkt liegt bei etwa 100 verifizierten Signaturen.
Um die Vorberechnung durchzuführen, können Sie die Methode precompute() auf einer VerifyingKey-Instanz aufrufen:```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.verifying_key
vk.precompute()
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
Sobald `precompute()` aufgerufen wurde, werden alle Signaturverifizierungen mit diesem Schlüssel
schneller ausgeführt.
## OpenSSL-Kompatibilität
Um Signaturen zu erzeugen, die von OpenSSL-Tools verifiziert werden können, oder um
Signaturen zu verifizieren, die von diesen Tools erzeugt wurden, verwenden Sie:```python
# openssl ecparam -name prime256v1 -genkey -out sk.pem
# openssl ec -in sk.pem -pubout -out vk.pem
# echo "data for signing" > data
# openssl dgst -sha256 -sign sk.pem -out data.sig data
# openssl dgst -sha256 -verify vk.pem -signature data.sig data
# openssl dgst -sha256 -prverify sk.pem -signature data.sig data
import hashlib
from ecdsa import SigningKey, VerifyingKey
from ecdsa.util import sigencode_der, sigdecode_der
with open("vk.pem") as f:
vk = VerifyingKey.from_pem(f.read())
with open("data", "rb") as f:
data = f.read()
with open("data.sig", "rb") as f:
signature = f.read()
assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der)
with open("sk.pem") as f:
sk = SigningKey.from_pem(f.read(), hashlib.sha256)
new_signature = sk.sign_deterministic(data, sigencode=sigencode_der)
with open("data.sig2", "wb") as f:
f.write(new_signature)
# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data
Hinweis: Falls Kompatibilität mit OpenSSL 1.0.0 oder früher erforderlich ist, können die
sigencode_string und sigdecode_string aus ecdsa.util zum
Schreiben bzw. Lesen der Signaturen verwendet werden.
Die Schlüssel können auch in einem Format geschrieben werden, das openssl verarbeiten kann:```python from ecdsa import SigningKey, VerifyingKey
with open("sk.pem") as f: sk = SigningKey.from_pem(f.read()) with open("sk.pem", "wb") as f: f.write(sk.to_pem())
with open("vk.pem") as f: vk = VerifyingKey.from_pem(f.read()) with open("vk.pem", "wb") as f: f.write(vk.to_pem())
## Entropie
Das Erstellen eines Signaturschlüssels mit `SigningKey.generate()` erfordert eine Form von
Entropie (im Gegensatz zu
`from_secret_exponent`/`from_string`/`from_der`/`from_pem`,
die deterministisch sind und keine Entropiequelle benötigen). Die Standardquelle
ist `os.urandom()`, aber du kannst jede andere Funktion übergeben, die sich
wie `os.urandom` verhält, als Argument `entropy=`, um etwas anderes zu tun. Das
kann in Unit-Tests nützlich sein, wenn du wiederholbare Ergebnisse erzielen möchtest. Der
`ecdsa.util.PRNG`-Dienstprogramm ist hier hilfreich: Es nimmt einen Seed und erzeugt daraus einen starken
pseudo-zufälligen Strom:```python
from ecdsa.util import PRNG
from ecdsa import SigningKey
rng1 = PRNG(b"seed")
sk1 = SigningKey.generate(entropy=rng1)
rng2 = PRNG(b"seed")
sk2 = SigningKey.generate(entropy=rng2)
# sk1 and sk2 are the same key
Ebenso erfordert die ECDSA-Signaturerzeugung eine Zufallszahl, und jede
Signatur muss eine andere verwenden (die Verwendung derselben Zahl zweimal
würde sofort den privaten Signaturschlüssel offenlegen). Die Methode sk.sign() akzeptiert ein
Argument entropy=, das sich genauso verhält wie SigningKey.generate(entropy=).
Wenn Sie SigningKey.sign_deterministic(data) anstelle von .sign(data) aufrufen,
erzeugt der Code eine deterministische Signatur anstelle einer zufälligen.
Dabei wird der Algorithmus aus RFC6979 verwendet, um sicher einen eindeutigen k-Wert zu erzeugen,
der aus dem privaten Schlüssel und der zu signierenden Nachricht abgeleitet wird. Jedes Mal, wenn Sie
dieselbe Nachricht mit demselben Schlüssel signieren, erhalten Sie dieselbe Signatur (unter Verwendung
desselben k).
Dies könnte in einer zukünftigen Version zur Standardeinstellung werden, da es nicht anfällig für Ausfälle der Entropiequelle ist.
Erstellen Sie ein NIST192p-Schlüsselpaar und speichern Sie beide sofort auf der Festplatte:```python from ecdsa import SigningKey sk = SigningKey.generate() vk = sk.verifying_key with open("private.pem", "wb") as f: f.write(sk.to_pem()) with open("public.pem", "wb") as f: f.write(vk.to_pem())
Laden Sie einen Signierschlüssel von der Festplatte, verwenden Sie ihn zum Signieren einer Nachricht (mit SHA-1) und schreiben Sie die Signatur auf die Festplatte:```python
from ecdsa import SigningKey
with open("private.pem") as f:
sk = SigningKey.from_pem(f.read())
with open("message", "rb") as f:
message = f.read()
sig = sk.sign(message)
with open("signature", "wb") as f:
f.write(sig)
Lade den Verifikationsschlüssel, die Nachricht und die Signatur von der Festplatte und verifiziere die Signatur (gehe von SHA-1-Hash aus):```python from ecdsa import VerifyingKey, BadSignatureError vk = VerifyingKey.from_pem(open("public.pem").read()) with open("message", "rb") as f: message = f.read() with open("signature", "rb") as f: sig = f.read() try: vk.verify(sig, message) print "good signature" except BadSignatureError: print "BAD SIGNATURE"
Erstelle ein NIST521p-Schlüsselpaar:```python
from ecdsa import SigningKey, NIST521p
sk = SigningKey.generate(curve=NIST521p)
vk = sk.verifying_key
Erstelle drei unabhängige Signaturschlüssel aus einem Master-Seed:```python from ecdsa import NIST192p, SigningKey from ecdsa.util import randrange_from_seed__trytryagain
def make_key_from_seed(seed, curve=NIST192p): secexp = randrange_from_seed__trytryagain(seed, curve.order) return SigningKey.from_secret_exponent(secexp, curve)
sk1 = make_key_from_seed("1:%s" % seed) sk2 = make_key_from_seed("2:%s" % seed) sk3 = make_key_from_seed("3:%s" % seed)
Laden Sie einen Verifizierungsschlüssel von der Festplatte und geben Sie ihn mithilfe der Hex-Kodierung im
unkomprimierten und komprimierten Format aus (definiert in den Standards X9.62 und SEC1):```python
from ecdsa import VerifyingKey
with open("public.pem") as f:
vk = VerifyingKey.from_pem(f.read())
print("uncompressed: {0}".format(vk.to_string("uncompressed").hex()))
print("compressed: {0}".format(vk.to_string("compressed").hex()))
Laden Sie einen Verifizierungsschlüssel aus einer Hex-Zeichenfolge im komprimierten Format, geben Sie unkomprimiert aus:```python from ecdsa import VerifyingKey, NIST256p
comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) print(vk.to_string("uncompressed").hex())
ECDH-Schlüsselaustausch mit der Gegenstelle:```python
from ecdsa import ECDH, NIST256p
ecdh = ECDH(curve=NIST256p)
ecdh.generate_private_key()
local_public_key = ecdh.get_public_key()
#send `local_public_key` to remote party and receive `remote_public_key` from remote party
with open("remote_public_key.pem") as e:
remote_public_key = e.read()
ecdh.load_received_public_key_pem(remote_public_key)
secret = ecdh.generate_sharedsecret_bytes()
brainpoolP512r1secp112r1secp112r2secp128r1secp160r1