Solidity ecrecover und web3j Sign.signMessage() sind nicht kompatibel, oder?

Die Verwendung von Web3j zum Signieren und Überprüfen einer Nachricht ist in Ordnung, aber wenn ich die von Web3j generierte Signatur verwende, um die Verwendung von ecrecover in Solidity zu überprüfen, scheint sie nicht richtig zu funktionieren. Die Unterzeichneradresse ist anders, wenn der Solidity-Code ausgeführt wird.

Bei der Suche nach einer Lösung fand ich heraus, dass die neuere Geth-Version beim Signieren die Zeichenfolge „\x19Ethereum Signed Message:\n32“ voranstellt.

Hinweis hier: Völlig verblüfft von ecrecover

Ich habe versucht, die Lösung in der Referenzfrage zu verwenden, aber es scheint nicht zu funktionieren. Meine Frage ist, was in Bezug auf Web3j anders ist? Kann mir jemand, der mit Web3j vertraut ist, ein funktionierendes Beispiel für das Signieren und Verifizieren von Nachrichten geben?

Hier ist eine vereinfachte Version des Vertrags, an dem ich gearbeitet habe:

pragma solidity ^0.4.0;

contract Auth {   

    string public name;

    function Auth(){
        name = "Auth 1.0";
    }

    function verify(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) constant returns (address) {
        address signer = ecrecover(_message, _v, _r, _s);
        return signer;
    }

    function verifyWithPrefix(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) constant returns (address) {
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";
        bytes32 prefixedHash = sha3(prefix, _message);
        address signer = ecrecover(prefixedHash, _v, _r, _s);
        return signer;
    }
}

Hier ist der Java-Code mit Web3j.

    // Message to sign
    String plainMessage = "hello world";
    byte[] hexMessage = Hash.sha3(plainMessage.getBytes());

    // Use java to sign and verify the signature
    Sign.SignatureData signMessage = Sign.signMessage(hexMessage, ALICE.getEcKeyPair());
    String pubKey = Sign.signedMessageToKey(hexMessage, signMessage).toString(16);
    String signerAddress = Keys.getAddress(pubKey);
    System.out.println("Signer address    : 0x"+ signerAddress);

    // Now use java signature to verify from the blockchain
    Bytes32 message = new Bytes32(hexMessage);
    Uint8 v = new Uint8(signMessage.getV());
    Bytes32 r = new Bytes32(signMessage.getR());
    Bytes32 s = new Bytes32(signMessage.getS());

    String address = contract.verify(message, v, r, s).get().getValue().toString(16);
    String address2 = contract.verifyWithPrefix(message, v, r, s).get().getValue().toString(16);
    System.out.println("Recovered address1 : 0x"+address);
    System.out.println("Recovered address2 : 0x"+address2);

Antworten (1)

Das Problem war tatsächlich doppeltes Hashing.

Betrachten Sie die Methode signMessage () von Web3j

Sign.SignatureData sig =Sign.signMessage(messageBytes, ecKeyPair);

und die Methode signedMessageToKey()

String pubKey = Sign.signedMessageToKey(messageBytes, sig).toString(16);

Diese Methoden hashen (sha3) intern die eingegebenen messageBytes vor dem Signieren und bei der Überprüfung. Dies funktioniert gut, wenn beide Signaturen mit Web3j selbst generiert und verifiziert werden. Aber dadurch unterscheidet sich die Signatur von Web3j von der sign()-Methode von geth.

Hier ist ein öffentlicher Kern mit der modifizierten Sign.java-Datei von Web3j, die verwendet werden kann, um das Problem des doppelten Hashings zu vermeiden: https://gist.github.com/xoriole/4c2a9630dba5a20ee28fb58edf193375