Die bitcoinj
API der Bibliothek bietet eine signMessage
Methode der ECKey
Klasse, die eine Signatur (r,s)
als 65 Byte codiert als Base64-String zurückgibt. Der Knackpunkt der Kodierung liegt im zusätzlichen führenden Byte, das eine Schlüsselwiederherstellung ermöglicht (die 64 Bytes allein (r,s)
reichen dafür nicht aus). Insbesondere enthält das führende Byte der Codierung Informationen über den Komprimierungsstatus des Signaturschlüssels.
Nun gehe ich davon aus, dass diese spezifische Signaturcodierung aus einem sehr guten Grund implementiert wurde, und ich vermute, dass es sich um eine Standardcodierung handelt, die in vielen anderen Bitcoin-Bibliotheken und im Kern vorhanden ist.
Wenn nun eine Signatur durch Schlüsselwiederherstellung verifiziert wird, verifyMessage
ignoriert das Verfahren den Komprimierungsstatus des wiederhergestellten Schlüssels und stellt einfach sicher, dass die entsprechenden elliptischen Kurvenpunkte übereinstimmen. Sicher, das ist eine ziemlich gute Prüfung, aber warum machen wir uns die Mühe, den Komprimierungsstatus eines Signaturschlüssels zu codieren, wenn wir ihn später ignorieren werden? Ist dies die Semantik, die andere bekannte Bibliotheken übernommen haben, oder tatsächlich der Kern? Zur Veranschaulichung hänge ich einen kleinen Ausschnitt an:
import org.bitcoinj.core.ECKey;
import java.security.SignatureException;
public class Test
{
public static void main(String[] args)
{
String message = "some arbitrary message";
ECKey k1 = new ECKey(); // random , compressed
ECKey k2 = k1.decompress();
// Signature (r,s) encoded as 65 bytes, with leading byte
// allowing key recovery (including compression status)
String sig1 = k1.signMessage(message);
String sig2 = k2.signMessage(message);
// compression status is encoded in signature => differing leading byte
System.out.println(sig1); // INgDhkt98Mme9m9AQ+nqtjyvjj ...
System.out.println(sig2); // HNgDhkt98Mme9m9AQ+nqtjyvjj ...
// signatures are verified succesfully
try
{
k1.verifyMessage(message, sig1); // compressed case
}
catch(SignatureException e)
{
System.out.println("it should not happen");
}
try
{
k2.verifyMessage(message, sig2); // uncompressed case
}
catch(SignatureException e)
{
System.out.println("it should not happen");
}
// in fact, compression status is ignored ...
try
{
k1.verifyMessage(message, sig2); // should it throw ?
}
catch(SignatureException e)
{
System.out.println("it does not happen");
}
}
}
Der Kilometerstand kann mit dieser Antwort variieren. Die kurze Antwort lautet wahrscheinlich nein, wenn Sie libbitcoin verwenden.
Begründung: Als ich vor über einem Jahr damit experimentierte, Gelder an eine unkomprimierte Adresse mit einer signierten Transaktion zu senden, konnten die entsprechenden komprimierten Schlüssel nicht verwendet werden, um dieselben Gelder auszugeben, das Gegenteil war auch der Fall. Das Gleichgewicht war abhängig von der Kompression einer Taste. Verwenden Sie beim Empfangen von Geldern mit einer unkomprimierten Adresse nur den zugehörigen unkomprimierten privaten Schlüssel zum Ausgeben. In ähnlicher Weise verwenden Sie beim Empfangen von Geldern mit einer komprimierten Adresse nur den zugehörigen komprimierten privaten Schlüssel zum Ausgeben.
Sven Williams
Pieter Wuille