Web3j签名并在Solidity智能合约中验签

源码地址:https://gitee.com/ttx_urey/web3j-sign

其他地方大多是使用web3的形式,web3j的基本没有

首先写一个简单智能合约用于验签测试

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract Sign {
    
    function genMsg(
        uint256 num,
        uint256[] memory list,
        address _address
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(num, list, _address));
    }

    function check(
        uint256 num,
        uint256[] memory list,
        address _address,
        bytes memory sig
    ) public pure returns (address) {
        return recoverSigner(genMsg(num,list,_address),sig);
    }

    function splitSignature(bytes memory sig)
        internal
        pure
        returns (
            uint8,
            bytes32,
            bytes32
        )
    {
        require(sig.length == 65);

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        return (v, r, s);
    }

    function recoverSigner(bytes32 message, bytes memory sig)
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;

        (v, r, s) = splitSignature(sig);

        return ecrecover(message, v, r, s);
    }
}

java签名代码

package org.urey.web3j;

import java.util.ArrayList;
import java.util.List;

import org.springframework.security.crypto.codec.Hex;
import org.web3j.abi.TypeEncoder;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.StaticArray;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;

public class Test {

    // 钱包私匙(对应的钱包地址为:0x5ebacac108d665819398e5c37e12b0162d781398)
    private static final String PRIVATE_KEY = "e62248374af86aa480f9cebd44f04cd02b915130d4fbda885a201488257b0a17";

    public static void main(String[] args) throws Exception {
        Uint256 num = new Uint256(11);

        List<Uint256> list = new ArrayList<Uint256>();
        list.add(new Uint256(2));
        list.add(new Uint256(8));
        list.add(new Uint256(555));
        StaticArray<Uint256> staticArray = new StaticArray<Uint256>(Uint256.class, list) {
        };

        Address address = new Address("0x6cd2Bf22B3CeaDfF6B8C226487265d81164396C5");

        StringBuilder msg = new StringBuilder();
        msg.append(TypeEncoder.encode(num));
        msg.append(TypeEncoder.encode(staticArray));

        msg.append(address.toString().substring(2, address.toString().length()));
        // System.out.println(TypeEncoder.encode(address));
        // 0000000000000000000000006cd2bf22b3ceadff6b8c226487265d81164396c5
        // 地址前面会补0,与solidity的abi.encodePacked不一致,
        System.out.println("msg:" + msg);

        byte[] bytes = Hex.decode(msg.toString());
        ECKeyPair keyPair = ECKeyPair.create(Hex.decode(PRIVATE_KEY));

        byte[] hash = Hash.sha3(bytes);
        System.out.println("hash:" + new String(Hex.encode(hash)));
        SignatureData signatureData = Sign.signMessage(hash, keyPair, false);
        // SignatureData signatureData = Sign.signMessage(bytes, keyPair);
        // 默认内部会再调用Hash.sha3

        String sign = "0x" + new String(Hex.encode(signatureData.getR())) + new String(Hex.encode(signatureData.getS())) + new String(Hex.encode(signatureData.getV()));
        System.out.println("sign:" + sign);
    }

}

运行签名代码,得到结果:

msg:000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000022b6cd2bf22b3ceadff6b8c226487265d81164396c5
hash:0899fc1cac856eae1183ed64c439d6f907693cd4547372c78a489df4c4df3c91
sign:0x78bfe6a6e88713ffb51c40b11ad638627f88425bda9166de24ff6dc81432e22272bf31badca6029cabbc0bd1409a1c1f37b9a82bd55a9f995ae29eaad45756a81b

msg相当于abi.encodePacked(num, list, _address);
hash相当于keccak256(abi.encodePacked(num, list, _address));

用num=11,list=[2,8,555],_address=0x6cd2Bf22B3CeaDfF6B8C226487265d81164396C5,sig=0x78bfe6a6e88713ffb51c40b11ad638627f88425bda9166de24ff6dc81432e22272bf31badca6029cabbc0bd1409a1c1f37b9a82bd55a9f995ae29eaad45756a81b这些参数调用智能合约的check方法,可以得到验签的结果为0x5EbacaC108D665819398E5c37E12b0162D781398(忽略地址大小写),与钱包地址一致