ETH钱包生成ECKeyPair的正确姿势

作者: 唠嗑008 | 来源:发表于2018-11-02 17:30 被阅读4次

    前段时间做了一下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;
        }
    

    相关文章

      网友评论

        本文标题:ETH钱包生成ECKeyPair的正确姿势

        本文链接:https://www.haomeiwen.com/subject/lcycxqtx.html