[Ethereum] Recover address from signature using web3j

signatureweb3jweb3js

I have signed a data string using the following code snippet in web3js

var data = "hello world";

web3.personal.sign(web3.fromUtf8(data), web3.eth.accounts[0], function(error, result) {
    if (!error) {
        console.log(data);
    } else {
        console.log(error);
    }
});

Now I am trying to verify the address used to sign said string from web3j

public static boolean verifyAddressFromSignature(String address, String signature) 
{
    byte[] signatureBytes = Hash.sha3(signature).getBytes();
    byte v = signatureBytes[64];
    if(v < 27) 
    { 
        v += 27; 
    }

    SignatureData sd = new SignatureData(v, (byte[]) Arrays.copyOfRange(signatureBytes, 0, 32), (byte[]) Arrays.copyOfRange(signatureBytes, 32, 64));

    String addressRecovered = null;
    boolean match = false;

    // Iterate for each possible key to recover
    for (int i = 0; i < 4; i++) 
    {
        BigInteger publicKey = Sign.recoverFromSignature((byte) i, new ECDSASignature(new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())), signature.getBytes());

        if (publicKey != null) 
        {
            addressRecovered = "0x" + Keys.getAddress(publicKey); 
            System.out.println(addressRecovered);
            if (addressRecovered.equals(address)) 
            {
                match = true;
                break;
            }
        }
    }

    return match;
}

However, none of the generated addresses match the one used to create the signature.

Can anyone shed any light on what I am doing wrong?

Best Answer

package org.web3j.crypto;

import java.math.BigInteger;
import java.util.Arrays;

import org.junit.jupiter.api.Test;

import org.web3j.crypto.Sign.SignatureData;
import org.web3j.utils.Numeric;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class ECRecoverTest {

    public static final String PERSONAL_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n";

    @Test
    public void testRecoverAddressFromSignature() {

        String signature =
                "0x2c6401216c9031b9a6fb8cbfccab4fcec6c951cdf40e2320108d1856eb532250576865fbcd452bcdc4c57321b619ed7a9cfd38bd973c3e1e0243ac2777fe9d5b1b";

        String address = "0x31b26e43651e9371c88af3d36c14cfd938baf4fd";
        String message = "v0G9u7huK4mJb2K1";

        String prefix = PERSONAL_MESSAGE_PREFIX + message.length();
        byte[] msgHash = Hash.sha3((prefix + message).getBytes());

        byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
        byte v = signatureBytes[64];
        if (v < 27) {
            v += 27;
        }

        SignatureData sd =
                new SignatureData(
                        v,
                        (byte[]) Arrays.copyOfRange(signatureBytes, 0, 32),
                        (byte[]) Arrays.copyOfRange(signatureBytes, 32, 64));

        String addressRecovered = null;
        boolean match = false;

        // Iterate for each possible key to recover
        for (int i = 0; i < 4; i++) {
            BigInteger publicKey =
                    Sign.recoverFromSignature(
                            (byte) i,
                            new ECDSASignature(
                                    new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())),
                            msgHash);

            if (publicKey != null) {
                addressRecovered = "0x" + Keys.getAddress(publicKey);

                if (addressRecovered.equals(address)) {
                    match = true;
                    break;
                }
            }
        }

        assertEquals(addressRecovered, (address));
        assertTrue(match);
    }
}

https://github.com/web3j/web3j/blob/master/crypto/src/test/java/org/web3j/crypto/ECRecoverTest.java