Web3j钱包 -- java版本

基于Web3j的钱包工具类,包括普通钱包的生成和加载,bip39钱包的生成和加载,bip39钱包签名和验证。

相关依赖

<dependency>
	<groupId>org.web3j</groupId>
	<artifactId>core</artifactId>
	<version>4.2.0</version>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.12</version>
	<optional>true</optional>
</dependency>

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.31</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.3</version>
</dependency>

<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-lang3</artifactId>
	<version>3.3.9</version>
</dependency>

<dependency>
	<groupId>org.junit.jupiter</groupId>
	<artifactId>junit-jupiter</artifactId>
	<version>5.5.2</version>
</dependency>

安全随机数

import org.web3j.crypto.LinuxSecureRandom;

import java.security.SecureRandom;

/**
 * @author ming
 * @version 1.0.0
 * @date 2020/9/23 16:05
 **/
public class SecureRandomUtils {
    private static final SecureRandom SECURE_RANDOM;
    private static int isAndroid;

    public static SecureRandom secureRandom() {
        return SECURE_RANDOM;
    }

    private static boolean isAndroidRuntime() {
        if (isAndroid == -1) {
            String runtime = System.getProperty("java.runtime.name");
            isAndroid = runtime != null && "Android Runtime".equals(runtime) ? 1 : 0;
        }

        return isAndroid == 1;
    }

    private SecureRandomUtils() {
    }

    static {
        if (isAndroidRuntime()) {
            new LinuxSecureRandom();
        }

        SECURE_RANDOM = new SecureRandom();
        isAndroid = -1;
    }
}

钱包工具

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.tld.admin.common.utils.SecureRandomUtils;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.crypto.*;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Random;

/**
 * 基于web3j的钱包工具类
 *
 * @author ming
 * @version 1.0.0
 * @date 2020/9/21 10:30
 **/
public class Web3jWalletUtils {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final SecureRandom SECURE_RANDOM = SecureRandomUtils.secureRandom();

    static {
        // 转换为格式化的json
        OBJECT_MAPPER.enable(SerializationFeature.INDENT_OUTPUT);
        // 如果json中有新增的字段并且是实体类类中不存在的,不报错
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public Web3jWalletUtils() {

    }

    /**
     * 创建普通钱包
     *
     * @param password       钱包密码
     * @param walletFilePath 钱包文件存储路径
     * @return CommonWallet
     * @throws Exception e
     */
    public CommonWallet generateCommonWallet(String password, String walletFilePath) throws Exception {
        try {
            String walletFileName = WalletUtils.generateNewWalletFile(password, new File(walletFilePath), false);
            String path = StringUtils.isNotBlank(walletFilePath) && File.separator.equals(walletFilePath.substring(walletFilePath.length() - 1)) ? walletFilePath + walletFileName : walletFilePath + File.separator + walletFileName;
            Credentials credentials = WalletUtils.loadCredentials(password, walletFilePath);
            String address = credentials.getAddress();
            BigInteger publicKey = credentials.getEcKeyPair().getPublicKey();
            BigInteger privateKey = credentials.getEcKeyPair().getPrivateKey();
            return new CommonWallet(address, password, privateKey, publicKey, path);
        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException | CipherException | IOException e) {
            throw new Exception(e);
        }
    }

    /**
     * 创建普通钱包
     *
     * @param password 密码
     * @return CommonWallet
     * @throws Exception e
     */
    public CommonWallet generateCommonWallet(String password) throws Exception {
        try {
            ECKeyPair ecKeyPair = Keys.createEcKeyPair();
            WalletFile walletFile = generateWalletFile(password, ecKeyPair, false);
            BigInteger publicKey = ecKeyPair.getPublicKey();
            BigInteger privateKey = ecKeyPair.getPrivateKey();
            return new CommonWallet(walletFile.getAddress(), JSON.toJSONString(walletFile), password, privateKey, publicKey);
        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException | CipherException e) {
            throw new Exception(e);
        }
    }

    /**
     * 从钱包json加载钱包
     *
     * @param password 钱包密码
     * @param json     钱包json
     * @return CommonWallet
     * @throws JsonProcessingException jsonProcessingException
     * @throws CipherException         cipherException
     */
    public CommonWallet loadCommonWalletFromJson(String password, String json) throws JsonProcessingException, CipherException {
        WalletFile walletFile = OBJECT_MAPPER.readValue(json, WalletFile.class);
        log.debug("获取到的钱包Json: " + JSON.toJSONString(walletFile));
        Credentials credentials = loadCredentials(password, walletFile);
        String address = credentials.getAddress();
        ECKeyPair ecKeyPair = credentials.getEcKeyPair();
        BigInteger privateKey = ecKeyPair.getPrivateKey();
        BigInteger publicKey = ecKeyPair.getPublicKey();
        return new CommonWallet(address, json, password, privateKey, publicKey);
    }

    /**
     * 从钱包文件加载钱包
     *
     * @param password       密码
     * @param walletFilePath 钱包文件路径
     * @return CommonWallet
     * @throws IOException     e
     * @throws CipherException e
     */
    public CommonWallet loadCommonWalletFromFile(String password, String walletFilePath) throws IOException, CipherException {
        Credentials credentials = WalletUtils.loadCredentials(password, walletFilePath);
        String address = credentials.getAddress();
        ECKeyPair ecKeyPair = credentials.getEcKeyPair();
        BigInteger privateKey = ecKeyPair.getPrivateKey();
        BigInteger publicKey = ecKeyPair.getPublicKey();
        return new CommonWallet(address, password, privateKey, publicKey, walletFilePath);
    }

    /**
     * 创建bip39钱包
     *
     * @param password 钱包密码
     * @return CommonWallet
     * @throws CipherException e
     */
    public Bip39Wallet2 generateBip39Wallet(String password) throws CipherException {
        byte[] initialEntropy = new byte[16];
        SECURE_RANDOM.nextBytes(initialEntropy);
        String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);
        byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
        ECKeyPair ecKeyPair = ECKeyPair.create(Hash.sha256(seed));
        WalletFile walletFile = generateWalletFile(password, ecKeyPair, false);
        return new Bip39Wallet2(walletFile.getAddress(), mnemonic, password, JSON.toJSONString(walletFile), ecKeyPair.getPrivateKey(), ecKeyPair.getPublicKey());
    }

    /**
     * 从json加载bip39钱包
     *
     * @param password 密码
     * @param mnemonic 助记词
     * @param json     钱包文件
     * @return Bip39Wallet2
     */
    public Bip39Wallet2 loadBip39WalletFromJson(String password, String mnemonic, String json) {
        Credentials credentials = WalletUtils.loadBip39Credentials(password, mnemonic);
        return new Bip39Wallet2(credentials.getAddress(), json, mnemonic, password, credentials.getEcKeyPair().getPrivateKey(), credentials.getEcKeyPair().getPublicKey());
    }

    /**
     * 从文件加载bip39钱包
     *
     * @param password       密码
     * @param mnemonic       助记词
     * @param walletFilePath 钱包文件
     * @return Bip39Wallet2
     */
    public Bip39Wallet2 loadBip39WalletFromFile(String password, String mnemonic, String walletFilePath) {
        Credentials credentials = WalletUtils.loadBip39Credentials(password, mnemonic);
        return new Bip39Wallet2(credentials.getAddress(), mnemonic, password, credentials.getEcKeyPair().getPrivateKey(), credentials.getEcKeyPair().getPublicKey(), walletFilePath);
    }

    /**
     * 创建bip39钱包
     *
     * @param password       钱包密码
     * @param walletFilePath 钱包文件路径
     * @return CommonWallet
     * @throws CipherException e
     */
    public Bip39Wallet2 generateBip39Wallet(String password, String walletFilePath) throws CipherException, IOException {
        Bip39Wallet bip39Wallet = WalletUtils.generateBip39Wallet(password, new File(walletFilePath));
        String mnemonic = bip39Wallet.getMnemonic();
        // 返回钱包文件名
        String filename = bip39Wallet.getFilename();
        Credentials credentials = WalletUtils.loadBip39Credentials(password, mnemonic);
        String address = credentials.getAddress();
        BigInteger publicKey = credentials.getEcKeyPair().getPublicKey();
        BigInteger privateKey = credentials.getEcKeyPair().getPrivateKey();
        String path = StringUtils.isNotBlank(walletFilePath) && File.separator.equals(walletFilePath.substring(walletFilePath.length() - 1)) ? walletFilePath + filename : walletFilePath + File.separator + filename;
        return new Bip39Wallet2(address, mnemonic, password, privateKey, publicKey, path);
    }

    public static Credentials loadCredentials(String password, WalletFile walletFile) throws CipherException {
        return Credentials.create(Wallet.decrypt(password, walletFile));
    }

    public static WalletFile generateWalletFile(String password, ECKeyPair ecKeyPair, boolean useFullScrypt) throws CipherException {
        WalletFile walletFile;
        if (useFullScrypt) {
            walletFile = Wallet.createStandard(password, ecKeyPair);
        } else {
            walletFile = Wallet.createLight(password, ecKeyPair);
        }
        return walletFile;
    }

    public String signTransaction(String json, ECKeyPair keyPair) {
        Sign.SignatureData signatureData = Sign.signMessage(json.getBytes(), keyPair);
        JSONObject signatureDataJson = new JSONObject();
        signatureDataJson.put("v", Byte.toString(signatureData.getV()));
        signatureDataJson.put("r", Numeric.toBigInt(signatureData.getR()));
        signatureDataJson.put("s", Numeric.toBigInt(signatureData.getS()));
        return signatureDataJson.toJSONString();
    }

    /**
     * verify data
     * get public key and get wallet address with sign
     *
     * @param data          明文数据
     * @param walletAddress 钱包地址
     * @param strSign       签名数据
     * @return boolean
     * @throws Exception e
     */
    public boolean verifyTransaction(String data, String walletAddress, String strSign) throws Exception {
        try {
            if (StringUtils.isBlank(data)) {
                return false;
            }

            JSONObject jsonSign = JSONObject.parseObject(strSign);
            if (jsonSign == null) {
                return false;
            }

            byte v = jsonSign.getByte("v");
            byte[] r = Numeric.toBytesPadded(jsonSign.getBigInteger("r"), 32);
            byte[] s = Numeric.toBytesPadded(jsonSign.getBigInteger("s"), 32);

            Sign.SignatureData signatureData = new Sign.SignatureData(v, r, s);

            BigInteger publicKey = Sign.signedMessageToKey(data.getBytes(), signatureData);
            return StringUtils.equalsIgnoreCase("0x" + Keys.getAddress(publicKey), walletAddress);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    public String generateRandomPassword() {
        return generateRandomPassword(8);
    }

    public String generateRandomPassword(Integer count) {
        if (ObjectUtils.isEmpty(count)) {
            throw new RuntimeException("count must setting");
        }
        StringBuilder codeNum = new StringBuilder();
        int[] code = new int[3];
        Random random = new Random();
        for (int i = 0; i < count; i++) {
            int num = random.nextInt(10) + 48;
            int uppercase = random.nextInt(26) + 65;
            int lowercase = random.nextInt(26) + 97;
            code[0] = num;
            code[1] = uppercase;
            code[2] = lowercase;
            codeNum.append((char) code[random.nextInt(3)]);
        }
        return codeNum.toString();
    }

    @Data
    @Accessors(chain = true)
    static class CommonWallet {
        private String address;
        private String json;
        private String password;
        private BigInteger privateKey;
        private String privateKeyHexStr;
        private BigInteger publicKey;
        private String publicKeyHexStr;
        private String path;

        public CommonWallet(String address, String json, String password, BigInteger privateKey, BigInteger publicKey) {
            this.address = address;
            this.json = json;
            this.password = password;
            this.privateKey = privateKey;
            this.publicKey = publicKey;
            this.path = "";
            this.setPrivateKeyHexStr(privateKey);
            this.setPublicKeyHexStr(publicKey);
        }

        public CommonWallet(String address, String password, BigInteger privateKey, BigInteger publicKey, String path) {
            this.address = address;
            this.json = "";
            this.password = password;
            this.privateKey = privateKey;
            this.publicKey = publicKey;
            this.path = path;
            this.setPrivateKeyHexStr(privateKey);
            this.setPublicKeyHexStr(publicKey);
        }

        public void setPrivateKeyHexStr(BigInteger privateKey) {
            this.privateKeyHexStr = Numeric.toHexStringWithPrefix(privateKey);
        }

        public void setPublicKeyHexStr(BigInteger publicKey) {
            this.publicKeyHexStr = Numeric.toHexStringWithPrefix(publicKey);
        }

    }

    @Data
    @Accessors(chain = true)
    static class Bip39Wallet2 {
        private String address;
        private String password;
        private String json;
        private BigInteger privateKey;
        private String privateKeyHexStr;
        private BigInteger publicKey;
        private String publicKeyHexStr;
        private String mnemonic;
        private String path;

        public Bip39Wallet2(String address, String mnemonic, String password, String json, BigInteger privateKey, BigInteger publicKey) {
            this.address = address;
            this.password = password;
            this.json = json;
            this.privateKey = privateKey;
            this.publicKey = publicKey;
            this.mnemonic = mnemonic;
            this.path = "";
            this.setPrivateKeyHexStr(privateKey);
            this.setPublicKeyHexStr(publicKey);
        }

        public Bip39Wallet2(String address, String mnemonic, String password, BigInteger privateKey, BigInteger publicKey, String path) {
            this.address = address;
            this.password = password;
            this.json = "";
            this.privateKey = privateKey;
            this.publicKey = publicKey;
            this.mnemonic = mnemonic;
            this.path = path;
            this.setPrivateKeyHexStr(privateKey);
            this.setPublicKeyHexStr(publicKey);
        }


        public void setPrivateKeyHexStr(BigInteger privateKey) {
            this.privateKeyHexStr = Numeric.toHexStringWithPrefix(privateKey);
        }

        public void setPublicKeyHexStr(BigInteger publicKey) {
            this.publicKeyHexStr = Numeric.toHexStringWithPrefix(publicKey);
        }
    }
}

测试方法

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;

import java.io.IOException;


class Web3jWalletUtilsTest {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    private static Web3jWalletUtils web3jWalletUtils;

    private static final String COMMON_WALLET_PATH = "E:\tmp\wallet_home\common_wallet";
    private static final String BIP39_WALLET_PATH = "E:\tmp\wallet_home\bip39_wallet";

    @BeforeEach
    void setUp() {
        web3jWalletUtils = new Web3jWalletUtils();
    }

    /**
     * 普通钱包生成和恢复 -- json
     *
     * @throws Exception e
     */
    @Test
    void generateCommonWalletTest() throws Exception {
        Web3jWalletUtils.CommonWallet commonWallet = web3jWalletUtils.generateCommonWallet("");
        log.debug(JSON.toJSONString(commonWallet));
        // 从json加载
        Web3jWalletUtils.CommonWallet commonWallet1 = web3jWalletUtils.loadCommonWalletFromJson("", commonWallet.getJson());
        log.debug("加载的json: " + commonWallet1.getJson());
    }

    /**
     * 普通钱包生成和恢复 -- file
     *
     * @throws Exception e
     */
    @Test
    void generateCommonWalletOnWalletPathTest() throws Exception {
        Web3jWalletUtils.CommonWallet commonWallet = web3jWalletUtils.generateCommonWallet("", COMMON_WALLET_PATH);
        log.debug("生成的普通钱包: " + JSON.toJSONString(commonWallet));
        // 从文件加载
        Web3jWalletUtils.CommonWallet commonWallet1 = web3jWalletUtils.loadCommonWalletFromFile("", commonWallet.getPath());
        log.debug("从文件加载的json: " + commonWallet1.getJson());
    }

    /**
     * bip39钱包生成和恢复 -- json
     *
     * @throws CipherException e
     */
    @Test
    void bip39Wallet2Test() throws CipherException {
        Web3jWalletUtils.Bip39Wallet2 bip39Wallet2 = web3jWalletUtils.generateBip39Wallet("");
        log.debug("生成的bip39钱包: " + JSON.toJSONString(bip39Wallet2));
        // 从json加载钱包
        Web3jWalletUtils.Bip39Wallet2 bip39Wallet21 = web3jWalletUtils.loadBip39WalletFromJson("", bip39Wallet2.getMnemonic(), bip39Wallet2.getJson());
        log.debug("从json加载的bip39钱包: " + JSON.toJSONString(bip39Wallet21));
    }

    /**
     * bip39钱包生成和恢复 -- file
     *
     * @throws CipherException e
     * @throws IOException     e
     */
    @Test
    void bip39Wallet2OnWalletPathTest() throws CipherException, IOException {
        Web3jWalletUtils.Bip39Wallet2 bip39Wallet2 = web3jWalletUtils.generateBip39Wallet("", BIP39_WALLET_PATH);
        log.debug("生成的bip39钱包" + JSON.toJSONString(bip39Wallet2));
        Web3jWalletUtils.Bip39Wallet2 bip39Wallet21 = web3jWalletUtils.loadBip39WalletFromFile("", bip39Wallet2.getMnemonic(), bip39Wallet2.getPath());
        log.debug("生成的bip39钱包" + JSON.toJSONString(bip39Wallet21));
    }

    /**
     * 生成随机密码
     */
    @Test
    void generateRandomPasswordTest() {
        log.debug(web3jWalletUtils.generateRandomPassword());
        log.debug(web3jWalletUtils.generateRandomPassword(16));
    }

    /**
     * bip39钱包签名和验证交易
     */
    @Test
    void bip39WalletSignAndVerifyTransaction() throws Exception {
        // TODO: 2020/9/24 生成bip39钱包
        Web3jWalletUtils.Bip39Wallet2 bip39Wallet2 = web3jWalletUtils.generateBip39Wallet("123456", BIP39_WALLET_PATH);
        log.debug("生成的bip39钱包" + JSON.toJSONString(bip39Wallet2));
        String password = bip39Wallet2.getPassword();
        String mnemonic = bip39Wallet2.getMnemonic();
        log.debug("钱包密码: " + password);
        log.debug("钱包助记词: " + mnemonic);

        // TODO: 2020/9/24 获取原始数据
        JSONObject data = new JSONObject();
        data.put("fromWalletAddress", bip39Wallet2.getAddress());
        data.put("toWalletAddress", "0x565fe768c659259abn45cf4f1081a663d091bcb9");
        data.put("value", "99.4");
        data.put("chargeWalletAddress", "0xdd05e23c39eead942bcv63fd388ffa13a1a28307");
        data.put("chargeValue", "0.6");
        String rawData = data.toJSONString();
        log.debug("原始数据 : " + rawData);

        Credentials credentials = WalletUtils.loadBip39Credentials(password, mnemonic);
        // TODO: 2020/9/24 对原始数据进行签名
        String sign = web3jWalletUtils.signTransaction(rawData, credentials.getEcKeyPair());
        // TODO: 2020/9/24 验证签名的数据
        boolean flag = web3jWalletUtils.verifyTransaction(rawData, bip39Wallet2.getAddress(), sign);
        log.debug("验签结果: " + flag);
    }

}
原文地址:https://www.cnblogs.com/jockming/p/13725576.html