3

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?

2

3 Answers 3

1
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

2
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Nov 18, 2021 at 0:15
  • What are these 4 keys its iterating through? is it always 4?
    – Tobi
    Feb 23 at 22:25
0

You are trying to verify the signature without providing what the message was which was hashed.. this is one more param you need to provide

0

Here's an implementation I wrote that works exactly like the JavaScript ethers.utils.verifyMessage(message, signature) and ethers.utils.recoverAddress(digest, signature) methods.

EthersUtils.java

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import org.web3j.crypto.ECDSASignature;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;
import org.web3j.utils.Numeric;

public class EthersUtils {
  private static final String MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n";

  public static String verifyMessage(String message, String signature) {
    return EthersUtils.recoverAddress(EthersUtils.hashMessage(message), signature);
  }

  public static String hashMessage(String message) {
    return Hash.sha3(
        Numeric.toHexStringNoPrefix(
            (EthersUtils.MESSAGE_PREFIX + message.length() + message).getBytes(StandardCharsets.UTF_8)));
  }

  public static String recoverAddress(String digest, String signature) {
    SignatureData signatureData = EthersUtils.getSignatureData(signature);
    int header = 0;
    for (byte b : signatureData.getV()) {
      header = (header << 8) + (b & 0xFF);
    }
    if (header < 27 || header > 34) {
      return null;
    }
    int recId = header - 27;
    BigInteger key = Sign.recoverFromSignature(
        recId,
        new ECDSASignature(
            new BigInteger(1, signatureData.getR()), new BigInteger(1, signatureData.getS())),
        Numeric.hexStringToByteArray(digest));
    if (key == null) {
      return null;
    }
    return ("0x" + Keys.getAddress(key)).trim();
  }

  private static SignatureData getSignatureData(String signature) {
    byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
    byte v = signatureBytes[64];
    if (v < 27) {
      v += 27;
    }
    byte[] r = (byte[]) Arrays.copyOfRange(signatureBytes, 0, 32);
    byte[] s = (byte[]) Arrays.copyOfRange(signatureBytes, 32, 64);
    return new SignatureData(v, r, s);
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy