前言:
HD 钱包全称为是分层确定性(Hierarchical Deterministic)钱包的缩写 HD Wallets。
首次创建 HD 钱包或者备份钱包时,会产生一个助记词,助记词是一连串的英⽂单词,这一串单词序列就可以创建种子,种子又可以创建所有的私钥。单词顺序也是钱包的备份,可以恢复钱包。而种⼦对应的就是所确定性钱包的随机数。
HD 钱包的优点在于只需要主公钥,就可以生成出任意数量的子公钥。也就是说,无需私钥介入(主私钥和子私钥),就能基于主公钥生成新(公钥)地址,而这些地址其实都能被主私钥所控制。
直接撸代码:
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.builder.fluent.Configurations; import org.bitcoinj.core.Base58; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicHierarchy; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.HDKeyDerivation; import org.bitcoinj.crypto.HDUtils; import org.bitcoinj.params.MainNetParams; import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.wallet.DeterministicKeyChain; import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.UnreadableWalletException; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bscoin.coldwallet.cointype.common.ConfigUtil; import com.bscoin.coldwallet.cointype.common.HDWallet; import com.bscoin.coldwallet.cointype.common.HashUtils; import com.bscoin.coldwallet.cointype.common.SecretOperation; public class HDWalletPK { private static Logger LOG = LoggerFactory.getLogger(HDWalletPK.class); static NetworkParameters params; static{ try { Configuration config = ConfigUtil.getInstance(); params = config.getBoolean("bitcoin.testnet") ? TestNet3Params.get() : MainNetParams.get(); LOG.info("=== [BTC] bitcoin client networkID:{} ===",params.getId()); } catch (Exception e) { LOG.info("=== [BTC] com.bscoin.coldwallet.cointype.btc.HDWalletPK:{} ===",e.getMessage(),e); } } /** * @throws IOException * @throws FileNotFoundException * @Title: createHDWalletByPATH * @param @param word 助记词 * @param @param passphrase 密码 * @param @param childNum 生成的hd钱包数量 * @param @param params * @param @return 参数 * @return List<HDWallet> 返回类型 * @throws */ public static List<HDWallet> createHDWalletByPATH(String word, String passphrase, int[] childNum) throws FileNotFoundException, IOException { List<HDWallet> wallet = new ArrayList<HDWallet>(); try { DeterministicSeed deterministicSeed = new DeterministicSeed(word, null, passphrase, 0L); DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build(); DeterministicKey main = deterministicKeyChain.getKeyByPath(HDUtils.parsePath("44H/0H"), true); DeterministicHierarchy tree = new DeterministicHierarchy(main); DeterministicKey rootKey = tree.getRootKey(); LOG.info("### [BTC] childs privKey , pubKey , address start ###"); for (int i = childNum[0], len = childNum[1]; i < len; i++) { DeterministicKey deriveChildKey = HDKeyDerivation.deriveChildKey(rootKey, new ChildNumber(i)); wallet.add(new HDWallet(deriveChildKey.getPathAsString(), deriveChildKey.getPrivateKeyAsWiF(params), Base58.encode(deriveChildKey.getPubKey()), ECKey.fromPrivate(deriveChildKey.getPrivKey()).toAddress(params).toBase58())); } LOG.info("### [BTC] childs privKey , pubKey , address end ###"); } catch (UnreadableWalletException e) { e.printStackTrace(); } return wallet; } /** * @Title: generateMnemonic * @param @param passphrase * @param @param params * @param @return * @param @throws IOException 参数 * @return String 返回类型 * @throws */ public static String generateMnemonic(String passphrase) throws IOException { StringBuilder words = new StringBuilder(); SecureRandom secureRandom = new SecureRandom(); long creationTimeSeconds = System.currentTimeMillis() / 1000; DeterministicSeed ds = new DeterministicSeed(secureRandom, 128, passphrase, creationTimeSeconds); for (String str : ds.getMnemonicCode()) { words.append(str).append(" "); } return words.toString().trim(); } /** * @Title: generateAddress 根据公钥生成地址 * @param @param publicKey * @param @return 参数 * @return String 返回类型 * @throws */ public static String generateAddress(String publicKey) { //1. 计算公钥的 SHA-256 哈希值 byte[] sha256Bytes = HashUtils.sha256(Base58.decode(publicKey)); //2. 取上一步结果,计算 RIPEMD-160 哈希值 RIPEMD160Digest digest = new RIPEMD160Digest(); digest.update(sha256Bytes, 0, sha256Bytes.length); byte[] ripemd160Bytes = new byte[digest.getDigestSize()]; digest.doFinal(ripemd160Bytes, 0); //3. 取上一步结果,前面加入地址版本号(主网版本号“0x00”) byte[] networkID = new BigInteger("00", 16).toByteArray(); byte[] extendedRipemd160Bytes = HashUtils.add(networkID, ripemd160Bytes); //4. 取上一步结果,计算 SHA-256 哈希值 byte[] oneceSha256Bytes = HashUtils.sha256(extendedRipemd160Bytes); //5. 取上一步结果,再计算一下 SHA-256 哈希值 byte[] twiceSha256Bytes = HashUtils.sha256(oneceSha256Bytes); //6. 取上一步结果的前4个字节(8位十六进制) byte[] checksum = new byte[4]; System.arraycopy(twiceSha256Bytes, 0, checksum, 0, 4); //7. 把这4个字节加在第5步的结果后面,作为校验 byte[] binaryAddressBytes = HashUtils.add(extendedRipemd160Bytes, checksum); //8. 把结果用 Base58 编码算法进行一次编码 return Base58.encode(binaryAddressBytes); } /** * 验证地址是否合法 * @param address * @return */ public static boolean verifyAddress(String address) { if (address.length() < 26 || address.length() > 35) { return false; } byte[] decoded = HashUtils.decodeBase58To25Bytes(address); if (null == decoded) { return false; } // 验证校验码 byte[] hash1 = HashUtils.sha256(Arrays.copyOfRange(decoded, 0, 21)); byte[] hash2 = HashUtils.sha256(hash1); return Arrays.equals(Arrays.copyOfRange(hash2, 0, 4), Arrays.copyOfRange(decoded, 21, 25)); } public static void main(String[] args) throws IOException { String s = generateMnemonic("xx");//生成助记次 int[] a = {
1,10};//根据助记词生成childID={1-10}的钱包地址 List<HDWallet> walls = createHDWalletByPATH(s, "123457",a); for (HDWallet hdWallet : walls) { System.out.println(hdWallet.getPubKey()); System.out.println(hdWallet.getPrivKey()); System.out.println(hdWallet.getAddress()); System.out.println("----------------------"); } } } 复制代码
HashUtil:
import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.stream.Stream; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.ArrayUtils; /** * @ClassName: HashUtils * @author DHing * */ public class HashUtils { /** * 加密字符集合 */ private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; /** * 使用 sha256 算法加密 * @param input * @return */ public static String sha256Hex(String input) { return DigestUtils.sha256Hex(input); } /** * 使用 sha256 hash 算法加密,返回一个 64 位的字符串 hash * @param input * @return */ public static String sha256Hex(byte[] input) { return DigestUtils.sha256Hex(input); } public static byte[] sha256(String input) { return DigestUtils.sha256(input); } public static byte[] sha256(byte[] input) { return DigestUtils.sha256(input); } /** * 两个byte[]数组相加 * * @param data1 * @param data2 * @return */ public static byte[] add(byte[] data1, byte[] data2) { byte[] result = new byte[data1.length + data2.length]; System.arraycopy(data1, 0, result, 0, data1.length); System.arraycopy(data2, 0, result, data1.length, data2.length); return result; } /** * 将多个字节数组合并成一个字节数组 * * @param bytes * @return */ public static byte[] merge(byte[]... bytes) { Stream<Byte> stream = Stream.of(); for (byte[] b : bytes) { stream = Stream.concat(stream, Arrays.stream(ArrayUtils.toObject(b))); } return ArrayUtils.toPrimitive(stream.toArray(Byte[]::new)); } /** * long 类型转 byte[] * * @param val * @return */ public static byte[] toBytes(long val) { return ByteBuffer.allocate(Long.BYTES).putLong(val).array(); } /** * 使用 Base58 把地址解码成 25 字节 * @param input * @return */ public static byte[] decodeBase58To25Bytes(String input) { BigInteger num = BigInteger.ZERO; for (char t : input.toCharArray()) { int p = ALPHABET.indexOf(t); if (p == -1) { return null; } num = num.multiply(BigInteger.valueOf(58)).add(BigInteger.valueOf(p)); } byte[] result = new byte[25]; byte[] numBytes = num.toByteArray(); System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length); return result; } } 复制代码
HDWallet:
package com.bscoin.coldwallet.cointype.common; import java.io.Serializable; import org.bitcoinj.core.NetworkParameters; public class HDWallet implements Serializable{ private static final long serialVersionUID = 1L; public HDWallet(){} public HDWallet(String path, String privKey, String pubKey, String address) { super(); this.path = path; this.privKey = privKey; this.pubKey = pubKey; this.address = address; } public HDWallet(String privKey, String pubKey, String address) { super(); this.privKey = privKey; this.pubKey = pubKey; this.address = address; } private String word; //助记词 private String path;//路径-标识位 private String passphrase; private String privKey; //私钥 private String pubKey; //公钥 private String address;//地址 public String getWord() { return word; } public void setWord(String word) { this.word = word; } public String getPrivKey() { return privKey; } public void setPrivKey(String privKey) { this.privKey = privKey; } public String getPubKey() { return pubKey; } public void setPubKey(String pubKey) { this.pubKey = pubKey; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPassphrase() { return passphrase; } public void setPassphrase(String passphrase) { this.passphrase = passphrase; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } } 复制代码
转载于:https://juejin.im/post/5cf6011ff265da1ba2524af7
今天的文章HD地址批量生成(java)分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/26861.html