如何从签名邮件中恢复地址?

How to recover an address from the signed message?

我使用 Trust SDK 对消息和交易进行签名。

我恢复了这样的交易(Trust.signTransaction()):

val tx = Geth.newTransactionFromRLP(Numeric.hexStringToByteArray(response.result))

其中 Geth - 框架 (org.ethereum.geth),版本 1.8.11。

如何从签名消息 (Trust.signMessage()) 中恢复数据(以及用户钱包的地址)?

Trust SDK 的开发者表示要查看'recovery'。但它是什么?框架?

非常感谢您。

我已经解决了问题。

1)需要使用这种方式:

signPersonalMessageCall = Trust.signPersonalMessage()
                .message("my message to be signed")
                .call(this)

2) 获取用户地址需要使用:

const val PERSONAL_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n"    

//...

fun recoverAddressFromSignature(message: String, signature: String): String? {
            val prefix = PERSONAL_MESSAGE_PREFIX + message.length
            val msgHash = Hash.sha3((prefix + message).toByteArray())

            val signatureBytes = Numeric.hexStringToByteArray(signature)
            var v = signatureBytes[64]

            if (v < 27) {
                v = (v + 27).toByte()
            }

            val sd = SignatureData(
                    v,
                    Arrays.copyOfRange(signatureBytes, 0, 32) as ByteArray,
                    Arrays.copyOfRange(signatureBytes, 32, 64) as ByteArray)

            var addressRecovered: String? = null

            // Iterate for each possible key to recover
            for (i in 0..3) {
                val publicKey = Sign.recoverFromSignature(
                        i,
                        ECDSASignature(BigInteger(1, sd.r), BigInteger(1, sd.s)),
                        msgHash)

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

                    Log.i(Core.APP_TAG, "*-*-* addressRecovered = $addressRecovered")
                }
            }

            return addressRecovered
        }

恢复的方法(在 Java)是从 here 抓取的。

这是我编写的实现,其工作方式与 JavaScript ethers.utils.verifyMessage(message, signature)ethers.utils.recoverAddress(digest, signature) 方法完全相同。

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);
  }
}