区块链研习社区块链技术与金融白话区块链

ETH钱包生成ECKeyPair的正确姿势

2018-11-02  本文已影响4人  唠嗑008

前段时间做了一下eth钱包开发的事情,现在项目基本结束了。空下来就去梳理一下之前遇到的一些关键问题,以及踩过的一些坑。

下面就和大家分享一下我遇到过的一个问题。

创建钱包


在钱包开发中,创建钱包大致分为以下几个步骤。

1、随机生成一组助记词
2、根据助记词生成种子 seed
3、根据种子seed生成ECKeyPair
4、根据ECKeyPair和密码创建钱包

我发现网上很多博客在生成ECKeyPair的时候,姿势不对。

网上很多教程的生成方式

ECKeyPair ecKeyPair= ECKeyPair.create(sha256(seed));

这种方式是Web3j提供的API,看上去是很简洁,用起来很方便,但实际上坑很大,根本不能在项目中使用,只能作为Demo。

在创建ETH钱包的过程,需要使用BIP32,BIP39,BIP44,3个协议。它们的作用如下:
BIP32:
定义了目前被广泛使用的 HD Wallet,生成种子seed 和公钥、私钥的。

BIP39:用于生成助记词

BIP44:
基于 BIP32 的系统,赋予树状结构中的各层特殊的意义。让同一个 seed 可以支援多币种、多帐户等。简单一点说,它就是指定币种规范的一个协议。它的格式如下:

m / purpose' / coin_type' / account' / change / address_index

其中的 purporse' 固定是 44',代表使用 BIP44。而coin_type'用来表示不同币种,例如 Bitcoin 就是 0',Ethereum 是 60'

目前市面上的Ethereum 钱包均采用以上 Bitcoin HD Wallet 的架构,用的都是BIP44:,具体是这样的m/44'/60'/0'/0/0

而Web3j那种创建方式,并没有采用BIP44,在项目中是不能采用这种方式的,但是依然被很多人采用,并且在网上传播。

下面是我验证这种方式的一个例子。

 private void test(String password, String mnemonics) {
        String path = mContext.getCacheDir() + "/MyWallet";
        //2种方式生成seed是一样的
//        byte[] seed = MnemonicUtils.generateSeed(mnemonics, "");
//        ECKeyPair ecKeyPair = ECKeyPair.create(sha256(seed));
        byte[] seed2 = new SeedCalculator().calculateSeed(mnemonics, "");
        ECKeyPair ecKeyPair2 = ECKeyPair.create(sha256(seed2));

        Log.i(TAG, "test2 pubk : " + ecKeyPair2.getPublicKey());
        Log.i(TAG, "test2 prek : " + ecKeyPair2.getPrivateKey());
        Log.i(TAG, "test2 address : " + Keys.getAddress(ecKeyPair2));
}

通过seed获取ECKeyPair,然后通过ECKeyPair获取公钥、私钥、地址。
结果是这样的,可以明显看出是有问题的。

PublicKey:
7244922113023370429973158797216818288186973573283250245018732882291753635193658294263389808990758395488598426777669619093953194711324283066023497720888144
privateKey:
51826596792659989115758568077400059679039347198031504935670220417030436567903
address : 
f81f6c5eb74f1b726e4bce7f4528c966e46dc2e3

注意:明文私钥是64位的。

正确的姿势


1、生成 seed
2、生成 master key
3、生成 child key
4、我们取第一组child key即m/44'/60'/0'/0/0 得到私钥,keystore及地址

完整代码如下,这段代码用到了BIP32,BIP39,BIP44

/**
     * generate key pair to create eth wallet_normal
     * 生成KeyPair , 用于创建钱包(助记词生成私钥)
     */
    public ECKeyPair generateKeyPair(String mnemonics) {
        // 1. we just need eth wallet_normal for now
        AddressIndex addressIndex = BIP44
                .m()
                .purpose44()
                .coinType(60)
                .account(0)
                .external()
                .address(0);
        // 2. calculate seed from mnemonics , then get master/root key ; Note that the bip39 passphrase we set "" for common
        byte[] seed = new SeedCalculator().calculateSeed(mnemonics, "");
        ExtendedPrivateKey rootKey = ExtendedPrivateKey.fromSeed(seed, Bitcoin.MAIN_NET);
        Log.i(TAG, "mnemonics:" + mnemonics);
        String extendedBase58 = rootKey.extendedBase58();
        Log.i(TAG, "extendedBase58:" + extendedBase58);

        // 3. get child private key deriving from master/root key
        ExtendedPrivateKey childPrivateKey = rootKey.derive(addressIndex, AddressIndex.DERIVATION);
        String childExtendedBase58 = childPrivateKey.extendedBase58();
        Log.i(TAG, "childExtendedBase58:" + childExtendedBase58);

        // 4. get key pair
        byte[] privateKeyBytes = childPrivateKey.getKey();
        ECKeyPair keyPair = ECKeyPair.create(privateKeyBytes);

        // we 've gotten what we need
        String privateKey = childPrivateKey.getPrivateKey();
        String publicKey = childPrivateKey.neuter().getPublicKey();
        String address = Keys.getAddress(keyPair);


        Log.i(TAG, "privateKey:" + privateKey);
        Log.i(TAG, "publicKey:" + publicKey);
        Log.i(TAG, "address:" + Constant.PREFIX_16 + address);

        return keyPair;
    }
上一篇 下一篇

猜你喜欢

热点阅读