HD wallet

651 阅读9分钟

密钥与地址

受众:我
目标:搞清楚私钥、keystore、种子与助记词,以及BIPbip32、bip39和bip4。

密码、私钥、keystore与助记词

密码

密码不是私钥,密码可以进行修改或重置。它主要用途有两个,一是转账时候的支付密码,二是用 keystore 导入钱包时需要输入的密码,用于解锁keystore。

私钥

以太坊私钥是256bit,相当于32字节,用16进制表示为64个字符,掌握私钥就掌握账号所有权。 在钱包应用程序中,解锁账号后可以导出私钥。

keystore

由于私钥不便于记忆,容易被盗,所以就出现了keystore。 keystore是私钥的加密版本,它是一个JSON文件,包含了加密后的私钥和一些其他信息,如算法类型、加密参数等。

助记词

由于私钥多了不便于记忆,所以出现了助记词,它把私钥用一组单词表示出来,方便记忆。 助记词可以获取相关联的多个私钥,但是通过私钥不可以推到出助记词。 助记词和私钥的关系,主要通过bip32、bip39和bip44三个协议来实现。

公钥、私钥和地址的关系

私钥

一个长度为256位(32字节)的随机数,可以认为是以太坊网络中的用户唯一ID,用途:

  • 签署交易。
  • 验证交易。
  • 生成公钥。
  • 控制地址中的资金。

如何生成? 私钥是一个32个字节的数, 生成一个私钥在本质上在1到2^256之间选一个数字。

const crypto = require('crypto');
const privateKey = crypto.randomBytes(32);

公钥

通过私钥生成,它是一个长度为64字节的字符串。公钥可以用来验证签名,以及代表用户的身份。 公钥可以通过私钥推导出来,但是不能通过公钥推导出私钥, 过程是单向的。 以太坊使用与比特币完全相同的椭圆曲线,称为 secp256k1。

如何生成?

import * as secp from '@noble/secp256k1';
const publicKey = secp.getPublicKey(privateKey);

地址

以太坊地址是唯一标识符 unique identifiers,它们是使用单向哈希函数(Keccak-256)从公钥或合约派生的。

  • 使用Keccak-256哈希算法从公钥生成地址。
  • 地址可用于验证签名的有效性。

如何生成?

const createKeccakHash = require('keccak');
const address = createKeccakHash('keccak256').update(publicKey).digest().slice(-20);

钱包

存储和管理用户私钥的软件,有浏览器扩展插件钱包和移动应用程序。

钱包分类

主要根据私钥的生成方式来区分:

  • 非确定性钱包:有随机数生成私钥,私钥和地址一一对应,且多个私钥彼此不相关。 传统的以太坊钱包,就是非确定性钱包,它以文件的形式存储私钥,每一个地址都生成一个新的钱包文件,备份和恢复都比较麻烦。

  • 确定性钱包:通过种子生成私钥,私钥和地址一一对应,私钥可恢复。 确定性钱包具有由单个主私钥(称为种子,也可以称为助记词)生成的私钥。在确定性钱包中,私钥是相互关联的,并且始终可以使用相同的种子进行复制。 种子表示为您可以写下的英语单词列表(通常为 12 个单词),以便在需要时恢复您的密钥。 确定性钱包的最先进形式是分层确定性(HD)钱包,基于比特币的 BIP-32 标准。键被称为「分层」,因为它们代表树结构。父密钥可以派生一组子密钥,然后子私钥可以派生另一系列孙密钥。

非确定性钱包

以太坊最初的钱包,通过随机数生成私钥,私钥和地址一一对应,且多个私钥彼此不相关。 且私钥存在不方便记忆,不方便在网络上传输等问题,所以以太坊钱包初期都是通过keyStore存储和读取私钥,每一个地址都生成一个新的钱包文件,只需要记住密码即可。

keyStore存储和读取原理
一个JSON文件,包含许多神奇的参数,与复杂的加密操作隐约相关。

{  
    "crypto" : {  
        "cipher" : "aes-128-ctr",  // 对称 AES 算法的名称
        "cipherparams" : {  
            "iv" : "83dbcc02d8ccb40e466191a123791e0e"  
        },  
        "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", // cipher加密后的以太坊私钥
        "kdf" : "scrypt",  // 一个密钥派生函数,用于让您使用密码加密密钥库文件;
        "kdfparams" : {  
            "dklen" : 32,  
            "n" : 262144,  
            "r" : 1,  
            "p" : 8,  
            "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"  
        },  
        "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"  // 用于验证密码的代码;
    },  
    "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",  
    "version" : 3  
}
  1. 通过cipher对称加密函数加密私钥,获得加密后的私钥密文ciphertext,cipher(cipherparams)=ciphertext
  2. 通过kdf密钥派生函数,用于从密码短语中计算(或“派生”)解密密钥,这里就需要输入密码,kdf(kdfparams+passphrase)=decryption-key
  3. 通过以上两步后,验证mac, SHA3–256(decryption-key+ciphertext) === mac
  4. 如果验证通过,则就可以解密密文(私钥)了,cipher(decryption-key) = decrypted ethereum private key

image.png

参考:julien-maffre.medium.com/what-is-an-…

HD(Hierarchical Deterministic Wallet)钱包

分层确定性钱包

公私钥推导:BIP32

用于生成HD钱包的标准协议,根据一个随机数种子通过分层确定性推导的方式得到 N 个私钥。

  • 种子:通常是一个随机数,它是HD钱包的根。从种子可以生成一个主私钥,然后通过一系列的推导操作生成子密钥对。
  • 推导路径:推导路径是一系列的推导操作,用于从根密钥生成子密钥对。推导路径是一个字符串,它描述了如何从根密钥到达特定的子密钥。
  • Extended Key:扩展密钥是一种包含了额外信息的密钥,它可以用于生成子密钥对。有两种类型的扩展密钥:扩展私钥(xprv)和扩展公钥(xpub)。
  • 硬化推导:硬化推导是一种推导方式,它使用了父密钥的私钥来生成子密钥对,而不是使用公钥。这样可以保证即使一个子密钥被泄露,也无法推导出其它子密钥。
  • 用途:BIP32标准使得在一个系统中管理大量地址变得更加便捷和安全,尤其在钱包应用中非常有用。

路径规范:BIP44

为路径约定了一个规范的含义(也扩展了对多币种的支持),BIP0044指定了包含5个预定义树状层级的结构: m / purpose' / coin' / account' / change / address_index

  • m是固定的
  • purpose是固定的,值为44(或者 0x8000002C)
  • coin 代表Coin type,代表币种,比如比特币为0,以太坊为60,完整列表.
  • account 代表账户索引,从0开始
  • change 常量0用于外部(收款地址),常量1用于内部(也称为找零地址)。外部用于在钱包外可见的地址(例如,用于接收付款)。内部链用于在钱包外部不可见的地址,用于返回交易变更。 (所以一般使用0)
  • address_index 代表地址索引,从0开始, 代表生成第几个地址,官方建议: 每个account下的address_index不要超过20.

助记词规范:BIP39

助记词是由 BIP-39 定义的过程生成的,该过程涉及从一些信息源开始,转换数据,然后将其映射到单词列表。

生成助记词

image.png

  1. 随机数生成128位数字
  2. 取hash sha256,取前四位,添加到后面得到132位
  3. 每11位切割一次,得到12个数字,每个数字对应一个单词

助记词 => 种子

image-1.png 这个过程使用密钥拉伸函数(pbkdf2),它将助记词和一个可选的密码(称为“salt”)作为输入,重复运算并生成一个512位的密钥种子。

  • salt:常量字符串 "mnemonic" 及一个可选的密码组成,注意使用不同密码,则拉伸函数在使用同一个助记词的情况下会产生一个不同的种子。

  • PBKDF2(Password-Based Key Derivation Function 2)是一种密码学函数,通常用于将用户提供的密码转换成密钥,以便在加密通信或存储中使用。它的设计目的是增强密码的安全性,即使用户选择了相对较弱的密码,也能生成一个强大的密钥。 工作流程:PBKDF2使用一个伪随机函数(通常是一个哈希函数,如SHA-256)来将密码和盐作为输入,并生成一个密钥。 具体流程如下:

    • 将密码和盐连接在一起,形成一个输入字符串。
    • 使用伪随机函数对输入字符串进行一次哈希。
    • 将上一步的哈希结果再次与盐连接,形成新的输入字符串,然后再次进行哈希。
    • 重复上述步骤,直到达到指定的迭代次数。
    • 最终的输出就是生成的密钥种子512位。

参考文档