• 14、创建/恢复ETH钱包身份


    借助网上的一段描述:

    若以银行账户为类比,这 5 个词分别对应内容如下:

    地址=银行卡号
    密码=银行卡密码
    私钥=银行卡号+银行卡密码
    助记词=银行卡号+银行卡密码
    Keystore+密码=银行卡号+银行卡密码
    Keystore ≠ 银行卡号

    1
    2
    implementation 'org.web3j:core:3.3.1-android'
    implementation 'io.github.novacrypto:BIP39:0.1.9'

    org.web3j:core 这个库是Java的,org.web3j:core:x-android 是兼容Android平台,所有接口和工具类都是为Java应用设计的,所以在Android上使用的时候要注意变通一下。

    创建数字身份

    创建钱包身份可以通过 WalletUtils 类来实现,它可以创建两种钱包:标准和 BIP39。

    可以通过 generateWalletFile 函数创建,直接保存为json文件,以下其他三个函数都是它的封装。

    在Android上不建议使用 WalletUtils 的这几个函数创建数字身份。

    1
    2
    3
    4
    WalletUtils.generateFullNewWalletFile();
    WalletUtils.generateLightNewWalletFile();
    WalletUtils.generateNewWalletFile();
    WalletUtils.generateWalletFile();

    generateFullNewWalletFile 使用N_STANDARD加密强度,在Android上会发送OOM,Android的处理速度也跟不上。

    generateLightNewWalletFile 相对来说比较轻量级,但是在我手机(红米4)上也花了21秒才创建完成,而加载为 Credentials 花了40秒。而在一台三星手机跑比较快,7秒左右。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public void (View view)  {

    try {
    String password = "123456";
    String path = Environment.getExternalStorageDirectory().getPath() + "/MyWallet";
    File fileDir = new File(path);
    if (!fileDir.exists()) {
    fileDir.mkdirs();
    }
    //生成钱包身份保存到目录下
    String fileName = null;

    Log.e(TAG, "startClickCreateDefault: "+fileDir.getPath() );
    fileName = WalletUtils.generateLightNewWalletFile("123456",fileDir);

    Log.e(TAG, "wallet fileName: " + fileName );

    //加载钱包身份
    Credentials credentials = WalletUtils.loadCredentials(password,path + "/" +fileName);
    Log.e(TAG, "getAddress: "+credentials.getAddress());
    Log.e(TAG, "getPrivateKey: "+credentials.getEcKeyPair().getPrivateKey());
    Log.e(TAG, "getPublicKey: "+credentials.getEcKeyPair().getPublicKey());
    } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
    } catch (NoSuchProviderException e) {
    e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
    e.printStackTrace();
    } catch (CipherException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    创建BIP39身份

    可以导出助记词,创建花了43秒,用助记词导入很快,只花了几秒。

    1
    WalletUtils.generateBip39Wallet();

    WalletUtils提供的这个方法在Android上闪退,只有自己写一个了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public void startClickCreateBip39(View view){

    String password = "123456";
    String path = Environment.getExternalStorageDirectory().getPath() + "/MyWallet";
    File fileDir = new File(path);
    if (!fileDir.exists()) {
    fileDir.mkdirs();
    }
    Log.e(TAG, "wallet start");
    //闪退
    //Bip39Wallet wallet = WalletUtils.generateBip39Wallet(password,fileDir);
    //String mnemonic = wallet.getMnemonic();
    //Log.e(TAG, "助记词wallet.getMnemonic(): " + mnemonic);
    //String fileName = wallet.getFilename();
    //Log.e(TAG, "fileName: " + fileName);
    try {

    StringBuilder sb = new StringBuilder();
    byte[] entropy = new byte[Words.TWELVE.byteLength()];
    new SecureRandom().nextBytes(entropy);
    new MnemonicGenerator(English.INSTANCE).createMnemonic(entropy, sb::append);
    String mnemonic = sb.toString();
    //String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);
    byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
    ECKeyPair privateKey = ECKeyPair.create(sha256(seed));

    String fileName = WalletUtils.generateWalletFile(password, privateKey, fileDir, false);
    Log.e(TAG, "fileName: " + fileName);
    Log.e(TAG, "助记词wallet.getMnemonic(): " + mnemonic);
    //加载钱包身份
    Credentials credentials = WalletUtils.loadBip39Credentials(password,mnemonic);
    Log.e(TAG, "getAddress: "+credentials.getAddress());
    Log.e(TAG, "getPrivateKey: "+credentials.getEcKeyPair().getPrivateKey());
    Log.e(TAG, "getPublicKey: "+credentials.getEcKeyPair().getPublicKey());

    } catch (CipherException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } catch (Exception e){
    e.printStackTrace();
    }

    }

    Android创建钱包

    上面这些感觉比较适合Java程序,我们跳进去看看就知道了,其实生成数字身份的代码是:

    1
    2
    3
    4
    public static WalletFile createLight(String password, ECKeyPair ecKeyPair)
    throws CipherException {
    return create(password, ecKeyPair, N_LIGHT, P_LIGHT);
    }

    针对Android,我们需要将生成的数字身份 WalletFile 转为 JSON (Keystore)保存到 SharedPreferences ,所以整理一个工具类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    大专栏  14、创建/恢复ETH钱包身份s="line">80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    import android.support.annotation.Nullable;
    import android.util.Log;

    import org.web3j.crypto.CipherException;
    import org.web3j.crypto.Credentials;
    import org.web3j.crypto.ECKeyPair;
    import org.web3j.crypto.Keys;
    import org.web3j.crypto.MnemonicUtils;
    import org.web3j.crypto.Wallet;
    import org.web3j.crypto.WalletFile;

    import java.io.IOException;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.NoSuchAlgorithmException;
    import java.security.NoSuchProviderException;
    import java.security.SecureRandom;

    import io.github.novacrypto.bip39.MnemonicGenerator;
    import io.github.novacrypto.bip39.Words;
    import io.github.novacrypto.bip39.wordlists.English;

    import static org.web3j.crypto.Hash.sha256;

    public class MyWalletTool {

    private final String TAG = getClass().getName();

    /**
    * 创建一个轻量级钱包,没有助记词
    * @param password
    * @return
    */
    public WalletFile createLightWallet(String password){

    WalletFile walletFile = null;
    try {
    walletFile = Wallet.createLight(password, Keys.createEcKeyPair());
    } catch (CipherException | NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
    e.printStackTrace();
    }
    return walletFile;
    }

    /**
    * 创建一个助记词
    * @return
    */
    public String createMnemonic(){

    StringBuilder sb = new StringBuilder();
    byte[] entropy = new byte[Words.TWELVE.byteLength()];
    new SecureRandom().nextBytes(entropy);
    new MnemonicGenerator(English.INSTANCE).createMnemonic(entropy, sb::append);
    return sb.toString();
    }

    /**
    * 创建一个带有助记词的轻量级钱包
    * @param password
    * @param mnemonic
    * @return
    */

    public WalletFile createBip39Wallet(String password,String mnemonic){

    WalletFile walletFile = null;
    try {
    byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
    Log.d(TAG, "createLight start...");
    walletFile = Wallet.createLight(password, ECKeyPair.create(sha256(seed)));
    Log.d(TAG, "createLight end.");
    } catch (Exception e){
    e.printStackTrace();
    }
    return walletFile;
    }



    /**
    * 生成凭证
    * @param password
    * @param walletFile
    * @return
    * @throws CipherException
    */
    public Credentials createCredentials(String password,String mnemonic){

    byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
    return Credentials.create(ECKeyPair.create(sha256(seed)));
    }

    public Credentials createCredentials(String password,WalletFile walletFile) throws CipherException {

    return Credentials.create(Wallet.decrypt(password, walletFile));
    }

    public Credentials createCredentials(String privateKey) {

    return Credentials.create(privateKey);
    }

    交易凭证Credentials

    web3j 中每一个交易都需要一个参数:CredentialsCredentials 实例化有三种方法,其中私钥权限最高,所以绝不能泄露自己的私钥和助记词,常用的是密码 + Keystore

    MyWalletTool调用的函数来看,交易凭证的实例化只需要以下之一:

    1. 私钥
    2. 助记词
    3. 密码 + Keystore

    私钥

    一个钱包只有一个私钥且不能修改

    为什么 私钥 单独可以实现实例化 Credentials

    Credentials 的构造函数参数是 ECKeyPairaddress

    1
    2
    3
    4
    private Credentials(ECKeyPair ecKeyPair, String address) {
    this.ecKeyPair = ecKeyPair;
    this.address = address;
    }

    address 可以通过 ECKeyPair 推导出来,而 ECKeyPair 的构造函数参数就是公钥和私钥

    1
    2
    3
    4
    public ECKeyPair(BigInteger privateKey, BigInteger publicKey) {
    this.privateKey = privateKey;
    this.publicKey = publicKey;
    }

    公钥可以通过私钥推导出来,所以可以直接实例化 Credentials

    1
    Sign.publicKeyFromPrivate(privateKey)

    助记词

    助记词是明文私钥的另一种表现形式,其目的是为了帮助用户记忆复杂的私钥

    Canache生成的一个助记词

    1
    2
    3
    助记词:jump dolphin leave reward allow farm gate hospital region diary seminar loan
    地址:0x7E728c371D66813434F340E6D473B212F506bA54
    私钥:6229413033912ab1f26e36f0aad7e1ea2b957de73cfedf788b9fff811192aa89

    imToken 可以成功导入钱包,但是用下面的 BIP39 标准的代码却不行(passphrase是加盐,这里为空)。

    1
    2
    3
    4
    byte[] seed = MnemonicUtils.generateSeed(mnemonic, passphrase);//passphrase=null
    ECKeyPair ecKeyPair = ECKeyPair.create(sha256(seed));
    System.out.println("private=" + ecKeyPair.getPrivateKey().toString());
    System.out.println("private=" + ecKeyPair.getPrivateKey().toString(16));

    结果是:

    1
    2
    private=27538423023524426157929608133615570842335693203949154557762660148101331275721
    private=3ce231f097447fe5d623b3a1f9a37e8c554ee014959903c4e2ebadf69ac7cfc9

    网上查资料说 imToken 用的是 BIP44 标准。后面再看看怎么搞,imToken核心码开源地址

    BIP44助记词创建和导入

    Keystore

    将私钥以加密的方式保存为一份 JSON 文件,这份 JSON 文件就是 Keystore,所以它就是加密后的私钥,它必须配合钱包密码才能使用该账号。

    1
    ECKeyPair ecKeyPair = Wallet.decrypt(password, walletFile);

    钱包开源项目

    1. A ethereum wallet like imToken for Android
    2. A beautiful, secure and native Ethereum Wallet for Android
    3. Lightweight JS Wallet for Node and the browser
    4. A plugin that turns Vault into an Ethereum wallet. Golang
  • 相关阅读:
    [Memcached]操作
    [Linux-CentOS7]安装Telnet
    PAT Advanced 1093 Count PAT's (25分)
    PAT Advanced 1065 A+B and C (64bit) (20分)
    PAT Advanced 1009 Product of Polynomials (25分)
    PAT Advanced 1008 Elevator (20分)
    PAT Advanced 1006 Sign In and Sign Out (25分)
    PAT Advanced 1002 A+B for Polynomials (25分)
    半年分布式处理回顾&机器学习(一)——线性回归
    PAT Advanced 1147 Heaps (30分)
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12147620.html
Copyright © 2020-2023  润新知