Kann ich R und S aus einer ECDSA-Signatur in Bitform extrahieren und umgekehrt?

4363
BenM

Ich habe das Forum durchsucht und verwandte Informationen gefunden, aber keine Antwort auf diese Frage. Ich arbeite in einer extrem bandbreitenbeschränkten Umgebung und möchte möglichst kleine ECDSA-Signaturen erstellen. Wir verwenden vorerst OpenSSL. Außerdem - da die Signaturdaten eine vorhersagbare Länge haben müssen - eine feste Größe.

Ich verstehe, dass die ECDSA-Signatur aus zwei Ganzzahlen S und R besteht, deren Bitlänge der Kurvengröße entspricht. Außerdem scheinen 6-7 Bytes hinzugefügt zu werden, wenn ich versuche, eine Signatur zu erstellen und die Größe aus dem cygwin wc -cBefehl zu lesen . Ich habe an vielen Stellen gelesen, dass dieser Overhead variieren kann - daher verlieren wir die Vorhersagbarkeit der Signaturlänge.

Mögliche Lösung - extrahieren Sie S und R und übertragen Sie sie in binärer (?) Form - nur als Bitstrom. Ich bin nicht sicher, ob dies möglich ist, weil ich denke, dass dies die Kompatibilität mit der OpenSSL-Bibliothek verliert. Daher müssten wir den Prozess umkehren und eine Signatur erstellen, die S und R in der von OpenSSL akzeptierten Kodierung verwendet.

Ich habe festgestellt, dass es eine -binaryOption in OpenSSL gibt, die mir eine leere Datei gab. Ich nahm an, dass ich auf dem falschen Weg war.

2
R und S sollten DER-kodiert sein. Sie könnten sie extrahieren und "roh" senden und am anderen Ende erneut kodieren. Wenn einer der Werte zu klein ist (Wahrscheinlichkeit $ 2 ^ {- 8} = 1/256 $), können Sie ihn auf seine maximale Größe (dh die Größe der Kurve) mit Nullen auffüllen. [Siehe den Bären für einige Details] (http://crypto.stackexchange.com/a/1797/23623) SEJPM vor 8 Jahren 0
Danke, wenn Decode / Reencode funktioniert, wäre das großartig. Nach einem Blick auf den Artikel bin ich mir nicht sicher: 1) Ist es wirklich möglich, von DER zu decodieren und nur S und R zu erhalten - wie wäre es mit den Metadaten der OpenSSL / anderen Bibliotheken? Gibt es diese nicht? Wenn es Metadaten gibt, denke ich, dass sie intakt bleiben müssen, aber ich würde mich sehr freuen, wenn dies nicht der Fall ist. 2) Bin ich der Meinung, dass DER eine Standardaktion decodiert, also kann ich einfach google nach Standard-Libs suchen? Ich würde mich sehr über weitere Ausführungen freuen. Ich habe wenig Erfahrung mit dieser Art von Implementierungen. BenM vor 8 Jahren 0
1) Ich bin mir nicht sicher, ich habe es noch nicht getan. Sie können sich jedoch anschauen, was Ihr DER-Decoder Ihnen bietet, und herausfinden, was Sie benötigen (wahrscheinlich die beiden Ganzzahlen in der Sequenz, die die richtige Größe haben. Wenn Metadaten vorhanden sind, wird dies höchstwahrscheinlich so etwas wie Kurveninformationen sein.) Sie können am anderen Ende wiederherstellen (nehme ich an). 2) Ja, [DER-Decodierung] (https://en.wikipedia.org/wiki/Distinguished_Encoding_Rules) ist eine sehr Standardoperation und Sie haben kein Problem, Decodierer zu finden ( Ich hoffe). SEJPM vor 8 Jahren 0

1 Antwort auf die Frage

3
Frode

Ich weiß, es ist alt, aber ich habe in letzter Zeit versucht, dieselbe Frage zu klären, also hier eine Art Antwort.

Nehmen wir an, Sie haben so eine Signatur erstellt:

$ openssl dgst -sha256 -sign private_secp160k1.pem foo.txt > sign.bin 

Da die Kurve 160 Bit beträgt, besteht die Signatur aus zwei 20-Byte-Nummern. Standardmäßig wird es von OpenSSL im binären DER-Format von ASN.1 geschrieben. Bei der Prüfung sollten wir mindestens 2 * 20 Bytes finden:

$ xxd -i < sign.bin 0x30, 0x2d, 0x02, 0x14, 0x22, 0xd0, 0x8b, 0xc1, 0x0d, 0x0b, 0x7b, 0xff, 0xe6, 0xc1, 0x77, 0xc1, 0xdc, 0xc4, 0x2f, 0x64, 0x05, 0x17, 0x71, 0xc8, 0x02, 0x15, 0x00, 0xdd, 0xf4, 0x67, 0x10, 0x39, 0x92, 0x1b, 0x13, 0xf2, 0x40, 0x20, 0xcd, 0x15, 0xe7, 0x6a, 0x63, 0x0b, 0x86, 0x07, 0xb6 

In dieser bestimmten DER-codierten Signatur sind 47 Byte enthalten. Eine mit DER oder PEM codierte Signatur kann mit dem asn1parseBefehl von OpenSSL untersucht werden, um herauszufinden, was die Bytes sind:

$ openssl asn1parse -inform DER -in sign.bin 0:d=0 hl=2 l= 45 cons: SEQUENCE  2:d=1 hl=2 l= 20 prim: INTEGER :22D08BC10D0B7BFFE6C177C1DCC42F64051771C8 24:d=1 hl=2 l= 21 prim: INTEGER :DDF4671039921B13F24020CD15E76A630B8607B6 

Kurz gesagt heißt es:

  • Bei Byte 0 gibt es einen DER-Header der Länge 2, der eine Folge von Elementen mit einer Gesamtlänge von 45 Bytes anzeigt.
  • Bei Byte 2 gibt es einen DER-Header der Länge 2, der ein INTEGER-Element der Länge 20 anzeigt. Das erste Element der SEQUENCE (20 Bytes werden dann in Hex gedruckt)
  • Bei Byte 24 gibt es einen DER-Header der Länge 2, der ein INTEGER-Element der Länge 21 angibt, das zweite Element der SEQUENCE (20 Bytes werden dann in Hex dargestellt).

(Das d = N bedeutet nur "Tiefe", daher haben die beiden INTEGER-Elemente d == 1, da sie Teil einer Sequenz sind.)

Schön die rohen Bytes von früher drucken, erkennt man die Elemente:

$ xxd -i < sign.bin 0x30, 0x2d, # Sequence of length 45 to follow (45 == 0x2d)  0x02, 0x14, # Integer of length 20 to follow (20 == 0x14) # Here come the 20 bytes: 0x22, 0xd0, 0x8b, 0xc1, 0x0d, 0x0b, 0x7b, 0xff, 0xe6, 0xc1,  0x77, 0xc1, 0xdc, 0xc4, 0x2f, 0x64, 0x05, 0x17, 0x71, 0xc8,  0x02, 0x15, # Integer of length 21 to follow (21 == 0x15) 0x00, # The first of the 21 integer bytes (see explanation below!) # Here come the remaining 20 bytes 0xdd, 0xf4, 0x67, 0x10, 0x39, 0x92, 0x1b, 0x13, 0xf2, 0x40,  0x20, 0xcd, 0x15, 0xe7, 0x6a, 0x63, 0x0b, 0x86, 0x07, 0xb6 

Warum ist also die zweite ganze Zahl 21 Bytes?

Integer in DER sind Zweierkomplement, wenn also das erste Byte der 20-Byte-Ganzzahl> 0x7F ist, wird ein zusätzliches 0x00-Byte vorangestellt, dh das erste Bit ist immer 0. Mit anderen Worten, während R und S 20-Byte-Zahlen sind, Die Kodierung in DER-Ganzzahlen kann ein zusätzliches Byte erfordern.

In OpenSSL enthält der Typ ECDSA_SIG zwei BIGNUMs. Sobald Sie die DER-Bytes mit entschlüsselt haben d2i_ECDSA_SIG, können Sie mit yourSig->rund auf sie zugreifen. yourSig->sDies ist <= 20 Bytes (für 160-Bit-Kurven).

Es ist auch möglich, dass R oder S weniger als 20 Bytes benötigen, wenn ihre ersten höchstwertigen Ziffern gleich Null sind. Sie können die für jedes Byte benötigte Anzahl von Bytes ermitteln BN_num_bytesund dann in ein reguläres BN_bn2binBytearray mit serialisieren.

ECDSA_SIG* ecSig = d2i_ECDSA_SIG(NULL, derBuf, derBufSize);  int rBytes = BN_num_bytes(ecSig->r), // Number of bytes needed for R sBytes = BN_num_bytes(ecSig->s); // Number of bytes needed for S  // ... unsigned char someBuffer[40] = ; BN_bn2bin( ecSig->r, someBuffer + 20 - rBytes ); // Place R first in the buffer BN_bn2bin( ecSig->s, someBuffer + 40 - sBytes ); // Place S last in the buffer 

Auf der Empfängerseite würden Sie dann einfach die ECDSA_SIG-Instanz neu erstellen, indem Sie its rund member setzen s:

ECDSA_SIG* ecSig = ECDSA_SIG_new(); BN_bin2bn( someBuffer, 20, ecSig->r ); BN_bin2bn( someBuffer + 20, 20, ecSig->s ); 
+1 dafür, weil ich geholfen habe, eine ähnliche Position bei einem anderen (noch verwandten) Problem zu erreichen, das ich hatte. Für zukünftige Sucher ist ein Format mit fester Länge zum Kodieren der * R * - und * S * -Werte * IEEE P1363 *, das die Bytes von * R * und * S * in zwei Hälften eines Bytearrays 'nach rechts' ausrichtet , was für jeden relevant sein kann, der Interop zwischen Java (der ASN.1 DER verwendet) und C # (der IEEE P1363 verwendet) ausführt. Weitere Informationen finden Sie unter https://www.codeproject.com/kb/security/cryptointeropsign.aspx ... jimbobmcgee vor 7 Jahren 0