以太坊账户

以一个账户举例:

路径:

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

m/44'/60'/0'/0

助记词:

lonely quote life good erode key benefit cube brass degree nurse april

私钥:

0x0c09d05b07006d4e7389ad962969eed6c429399ef6608aab92407e85f33d8fb3

公钥:

0x037549b8496362e87decad41c543e9ee868189fb191ebb4c5cd668b4b251b7b9e2

地址:

0xe1DA06fff8Ca2Dc3bE69276B47501F89112dCF96

以太坊钱包使用户用来管理以太坊上面的钱包账户。一个钱包账户有以下信息:私钥、公钥和地址。

其中,私钥的另一种形式又有Keystore的形式。

创建账户

创建账号关键是生成一个私钥, 私钥是一个32个字节的数, 生成一个私钥在本质上在1到2^{256}之间选一个数字。

因此生成密钥的第一步也是最重要的一步,是要找到足够安全的熵源,即随机性来源,只要选取的结果是不可预测或不可重复的,那么选取数字的具体方法并不重要。

比如可以掷硬币256次,用纸和笔记录正反面并转换为0和1,随机得到的256位二进制数字可作为钱包的私钥。

从编程的角度来看,一般是通过在一个密码学安全的随机源(不建议大家自己去写一个随机数)中取出一长串随机字节,对其使用SHA256哈希算法进行运算,这样就可以方便地产生一个256位的数字。

形象说法

  1. 地址=银行卡号
  2. 密码=银行卡密码
  3. 私钥=银行卡号+银行卡密码
  4. 助记词=银行卡号+银行卡密码
  5. Keystore+密码=银行卡号+银行卡密码

备份钱包

1、既然私钥、助记词、Keystore+密码」如此重要,那么如何进行保存呢,最安全的方法就是:手抄纸上。

2、由于 Keystore 内容较多,手抄不方便,保存在电脑上也不安全,因此可以不对 Keystore 进行备份,只手抄私钥、助记词就足够了,手抄备份要注意以下几点:

(1)多抄几份,分别放在不同的安全区域,并告诉家人。

(2)对手抄内容进行验证,导入钱包看能不能成功,防止抄写错误。

(3)备份信息不要在联网设备上进行传播。

私钥

0x0c09d05b07006d4e7389ad962969eed6c429399ef6608aab92407e85f33d8fb3

16进制

长度固定:64个16进制数,32个字节

随机性,2^{256}次方种(256位二进制数、32个字节数、64个16进制数)

总之,私钥是一个32个字节的数, 生成一个私钥在本质上在1到2^{256}之间选一个数字,即64位长度的16进制字符组成

导出私钥

创建钱包后,输入密码可以导出私钥,这个私钥属于明文私钥,由 64 位字符串组成,一个钱包只有一个私钥且不能修改。

导入私钥

在导入钱包时,输入私钥并设置一个密码(不用输入原密码),就能进入钱包并拥有这个钱包的掌控权,就可以把钱包中的代币转移走。

公钥

1、长度:40

2、以太坊的公钥以0x开头

地址

1、创建钱包后会生成一个以 0x 开头的 42 位字符串,这个字符串就是钱包地址。也就是20的16进制的字符组成。

2、一个地址就是一个钱包账户,可以公开,用于转账(转入、转出)和查询账户余额。相当于你的银行卡账号。

3、地址不是公钥,但是地址是由公钥生成的,而公钥又是由私钥生辰的,这个过程是不可逆的。

钱包地址形式

以太坊,0x开头(包括基于以太坊平台代币)。形如

0x0bac871ed2677c4b790065f20bdb20a16fdd9dd3

比特币:a. 普通地址:1开头。b. 隔离见证地址:3开头

瑞波币地址:r开头。

莱特币地址:L开头。

keystroe

keystore常见于以太坊钱包,是你独有的、用于签署交易的以太坊私钥的加密文件。

keystore是一串Json格式的字符串,可以用任何以太坊钱包打开它。keystore必须配合你的钱包密码来使用,备份了keystore同时别忘了备份钱包的密码。
用户可以使用备份的助记词,重新导入imToken之类的钱包工具,用新的密码生成一个新的Keystore,可以用这种方法来修改钱包密码

本质是加密后的私钥。

形式

  {
  "address": "fa1d10334277d2a8b9115c0369a6511605cd47f5",
  "id": "4cea804f-db18-4820-8338-e29c9f9b6f73",
  "version": 3,
  "crypto": {
    "cipher": "aes-128-ctr",
    "cipherparams": {
      "iv": "9cdf057d21539030ff261d0951eefe10"
    },
    "ciphertext": "4fa65d6ecd41af51f958c1682a512217b09e26791b63282ba4e104e66f6876a5",
    "kdf": "scrypt",
    "kdfparams": {
      "dklen": 32,
      "n": 4096,
      "p": 6,
      "r": 8,
      "salt": "040b2d729fd672e6b2406fc56c0e1e51a3d87cc1aa45199b92863462c5f21634"
    },
    "mac": "dfb33788f5f336e463bfe348264d31d1db0e8fb24a35d40658db23745ccaab8c"
  }
}

备份

钱包里有一个备份 keystore 功能,选择备份 keystore,输入密码,会出现一大段字符,这个就是 keystore。

用途

1、Keystore必须配合你的钱包密码来使用,如果只是备份了KS,忘记密码,这个账户也就没了……

2、直接使用以太坊钱包,很少会直接看到自己的私钥,而是让你备份Keystore, 配合钱包密码来使用。即使黑客获取了你的Keystore, 还要破解钱包密码也还是有一定难度的,当然钱包密码也别设太简单。

导入钱包

1、在导入钱包中,输入 keystore 和密码,就能进入钱包了。

2、需要说明的是,这个密码是本手机原来设置的本钱包密码, 这一点和用私钥或助记词导入钱包不一样,用私钥或助记词导入钱包,不需要知道原密码,直接重置密码。

转账

助记词

其实助记词是分层确定性钱包 (Hierarchical Deterministic, 我们也称 HD-Wallet) 特有的私钥表现形式

1、创建钱包后,会出现一个备份助记词功能,选择备份助记词,输入密码,会出现 12 个单词,每个单词之间有一个空格,这个就是助记词,一个钱包只有一个助记词且不能修改。

2、助记词是私钥的另一种表现形式,具有和私钥同样的功能,在导入钱包中,输入助记词并设置一个密码(不用输入原密码),就能进入钱包并拥有这个钱包的掌控权,就可以把钱包中的代币转移走。

3、助记词是需要配合路径使用的, 相同的助记词配合不同的路径也会生成不同的地址

备份助记词

一般来说,钱包不保留助记词信息,在创建完成后,需要备份助记词。不备份助记词的钱包,是不给用户使用的。

一旦备份助记词助记词只能备份一次,备份后,在钱包中再也不会显示,因此在备份时一定要抄写下来。

导入钱包

通过助记词导入钱包

    /**
     * 此方式导入到的钱包时轻钱包
     *
     * @param walletName
     * @param mnemonicCode
     * @param password
     * @param callback
     */
    //导入钱包--助记词---KS
    public static void importWalletByMnemonicCode(String walletName, List<String> mnemonicCode, String password, OnImportMnemonicCallback callback) {
        String[] pathArray = ETH_TYPE.split("/");
        String passphrase = "";
        long creationTimeSeconds = System.currentTimeMillis() / 1000;
        DeterministicSeed ds = new DeterministicSeed(mnemonicCode, null, passphrase, creationTimeSeconds);
        //种子
        byte[] seedBytes = ds.getSeedBytes();

        if (seedBytes == null) {
            callback.onImportMnemonicFailure(ERROR_CREATE_WALLET_FAILURE);
            return;
        }
        DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);
        for (int i = 1; i < pathArray.length; i++) {
            ChildNumber childNumber;
            if (pathArray[i].endsWith("'")) {
                int number = Integer.parseInt(pathArray[i].substring(0,
                        pathArray[i].length() - 1));
                childNumber = new ChildNumber(number, true);
            } else {
                int number = Integer.parseInt(pathArray[i]);
                childNumber = new ChildNumber(number, false);
            }
            dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber);
        }

        ECKeyPair ecKeyPair = ECKeyPair.create(dkKey.getPrivKeyBytes());

        WalletBeanNew walletBean = new WalletBeanNew();
//            WalletFile walletFile = Wallet.create(password, ecKeyPair, 1024, 1); // WalletUtils. .generateNewWalletFile();
        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        walletBean.setPrivateKey(privateKey);
        walletBean.setPublic_key(publicKey);
        LoggerUtils.i("私钥: " + privateKey);
        try {
            String keystore = WalletUtils.generateWalletFile(password, ecKeyPair, new File(MySDK.getKeystoreDir()), false);
            keystore = MySDK.getKeystoreDir() + "/" + keystore;
            walletBean.setKeystore(keystore);
            LoggerUtils.i("钱包keystore: " + keystore);
            if (StringUtils.isEmpty(walletName)) {
                walletName = generateNewWalletName();
            }
            walletBean.setName(walletName);
//            String addr1 = walletFile.getAddress();
            String addr2 = Keys.getAddress(ecKeyPair);
            String walletAddress = Keys.toChecksumAddress(addr2);
            walletBean.setAddress(walletAddress);//设置钱包地址
            LoggerUtils.i("钱包地址: " + walletAddress);
            walletBean.setPassword(password);


            StringBuilder sb = new StringBuilder();
            for (String mnemonic : mnemonicCode) {
                sb.append(mnemonic);
                sb.append(" ");
            }
            LoggerUtils.i("助记词 === " + sb.toString());
            String mnemonicWord = sb.toString();
            walletBean.setMnemonic(mnemonicWord);
            walletBean.setMIconIndex(getNum(7));
            callback.onImportMnemonicSuccess(walletBean);
        } catch (CipherException | IOException e) {
            callback.onImportMnemonicFailure(e.getMessage());
        }

    }

通过keystore导入钱包

 /**
     * 导入钱包--KS
     * 此方式导入到的钱包要判断是不是以太坊标准钱包 使用{@link #loadCredentialsByP(int, String, File)}
     *
     * @param walletName
     * @param password
     * @param keystoreContent
     * @param callback
     */

    public static void importWalletByKeyStore(final String walletName, final String password, final KeystoreBean keystoreBean,String keystoreContent, final OnCreateWalletCallback callback) {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    if (keystoreBean == null) {
                        MySDK.getHandler().post(() -> callback.onCreateFailure(ERROR_KEYSTORE));
                        return;
                    }
                    final WalletBeanNew bean = new WalletBeanNew();

                    String path = MySDK.getKeystoreDir() + "/" + getWalletFileName(keystoreBean.getAddress());
                    File file = new File(path);
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    FileOutputStream out = new FileOutputStream(file, false); //如果追加方式用true,此处覆盖
//                    StringBuffer sb = new StringBuffer();
                    byte[] bytes = keystoreContent.getBytes();
                    out.write(bytes);
//                out.write(sb.toString().getBytes("utf-8"));//注意需要转换对应的字符集
                    out.close();
                    //创建钱包
                    int p = keystoreBean.getCrypto().getKdfparams().getP();
                    LoggerUtils.i("p = " + p);
                    Credentials credentials = loadCredentialsByP(p, password, file);
                    if (p == 6) {
                        bean.setLight(true);
                    } else {
                        bean.setLight(false);
                    }
                    ECKeyPair keyPair = credentials.getEcKeyPair();
                    String wallet_name = walletName;
                    if (wallet_name.equals("")) {
                        //兼容SDK
                        wallet_name = "new-wallet" + RandomUntil.getSmallLetter(3);
                    }
                    bean.setName(wallet_name);
                    bean.setPrivateKey(keyPair.getPrivateKey().toString(16));//私钥
                    bean.setPublic_key(keyPair.getPublicKey().toString(16));//公钥
                    bean.setAddress("0x" + Keys.getAddress(keyPair)); //地址
                    bean.setPassword(password); //密码
                    bean.setKeystore(path);
                    bean.setMIconIndex(getNum(7));
                    MySDK.getHandler().post(() -> callback.onCreateSuccess(bean));
                } catch (IOException | CipherException | NullPointerException | OutOfMemoryError e) {
                    MySDK.getHandler().post(() -> {
                        LoggerUtils.e(e.getMessage() + "  " + password);
                        callback.onCreateFailure(e.getMessage());
                    });
                }
            }
        }.start();

    }

通过私钥导入钱包

    /**
     * @param walletName 钱包名字
     * @param privateKey 钱包私钥
     * @param password   钱包密码
     * @param callback   导入结果的回调
     */
    //导入钱包--私钥---产生KS
    public static void importPrivateKey(String walletName, final String privateKey, final String password, final OnCreateWalletCallback callback) {

        try {
            final WalletBeanNew wallet = new WalletBeanNew();
            String name = walletName;
            if (name.equals("")) {
                name = "new-wallet" + RandomUntil.getSmallLetter(3);
            }
            BigInteger key = new BigInteger(privateKey, 16);
            ECKeyPair keyPair = ECKeyPair.create(key);
            String private_key = keyPair.getPrivateKey().toString(16);
            wallet.setPrivateKey(private_key);
            wallet.setPublic_key(keyPair.getPublicKey().toString(16));
            wallet.setAddress("0x" + Keys.getAddress(keyPair));
            wallet.setName(name);
            wallet.setMnemonic("");
            wallet.setPassword(password);
            String keystore = WalletUtils.generateWalletFile(password, keyPair, new File(MySDK.getKeystoreDir()), false);
            keystore = MySDK.getKeystoreDir() + "/" + keystore;
            wallet.setKeystore(keystore);
            wallet.setMIconIndex(getNum(7));
            MySDK.getHandler().post(() -> callback.onCreateSuccess(wallet));

        } catch (CipherException | IOException e) {
            MySDK.getHandler().post(() -> callback.onCreateFailure(e.getMessage()));
        }
    }

由于源码涉及到具体业务,这里不放出源码,如有问题,可以再留言区交流,谢谢指证!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352