区块链入门 ② - 钱包
钱包
概述
-
定义
- 广义上讲,钱包是一个应用程序,为用户提供交互界面。钱包控制用户访问权限,管理私钥和地址,跟踪余额以及创建和签名交易。
- 狭义上讲,从程序员的角度来看,“钱包”是指用于存储和管理用户私钥的数据结构,也是本章所指的钱包含义。
-
钱包类型
-
第一种类型是非确定性钱包(nondeterministic wallet),其中每个私钥都是从随机数独立生成的,密钥彼此之间无关联。
-
必须备份所有私钥。
-
如果为了减少备份而复用地址,这会将多个交易与此地址关联在一起,这样会增加隐私泄露风险。
-
除了简单的测试之外,不推荐非确定性钱包。
-
-
第二种类型是确定性钱包(deterministic wallet),其中所有的私钥都是从一个主私钥派生出来,这个主私钥即为种子(seed)。该类型钱包中所有私钥都相互关联,如果有原始种子,则可以再次生成全部私钥。
-
1 型确定性钱包
-
分层确定性钱包
-
优点一:树状结构可以用来表达额外的组织含义。
-
优点二:可以让用户去创建一系列公钥而无须知晓相对应的私钥。(见子公钥的派生)
-
-
-
注记词
- 助记词编码是表示确定性钱包的种子的英语单词序列。
- 不同于“脑钱包”,脑钱包由用户选择的单词组成,而助记词是由钱包随机创建并展现给用户的。
- 创建注记词过程
- 创建一个 128~256 位的随机数(熵)。
- 提取随机数的 SHA256 散列值的前 x 位(x 等于随机数位数除以 32)作为校验码。
- 将校验码添加到随机序列的末尾。
- 将序列按 11 位进行划分。
- 将每 11 位的值映射到一个含 2048($2^{11}$)个单词的预定义字典中。
- 生成的有顺序的单词组就是助记词。
-
从注记词生成种子
- 助记词表示长度为 128~256 位的熵(随机数)。通过使用密钥延伸函数 PBKDF2,熵被用于派生出更长的(512 位)种子。将所得的种子用于构建确定性钱包并派生密钥。
- PBKDF2 密钥延伸函数的第一个参数是从步骤 6 生成的助记词。
- PBKDF2 密钥延伸函数的第二个参数是盐。由固定的字符串“mnemonic”与可选的用户输入的密码字符串连接组成。
- PBKDF2 将助记词和盐作为参数,调用 2048 次 HMAC-SHA512 散列算法,生成一个 512 位的值作为其延伸的最终输出。这个 512 位的值就是种子。
创建主私钥和主链码
- 根种子(还有可选密码)输入到 HMAC-SHA512 算法中就可以得到一个可用来创造主私钥(m)和主链码(c)的散列值。
- 主私钥(m)可以生成相对应的主公钥(M)。
- 主链码(c)用于从父私钥创造子私钥的那个函数中引入随机数(熵)。
子密钥的派生
分层确定性钱包使用 CKD(child key derivation,子密钥派生)函数从父密钥派生出子密钥。
-
子密钥派生函数是基于单向散列函数的,这个函数结合了:
- 一个父私钥或者父公钥(ECDSA 未压缩密钥)
- 一个 256bit 的链码,链码是用来给这个确定性过程引入随机数据的,以便知道索引编号和子私钥也不足以派生其他子私钥。
- 一个 32bit 的索引码,用于按顺序生成相应的子私钥和子链编码。
-
因为衍生方程是单向方程,所以子密钥不能被用来发现他们的母密钥。子密钥也不能用来发现他们的相同层级的姊妹密钥。
-
没有子链码的话,子密钥也不能用来衍生出任何孙密钥。你需要同时有子密钥以及对应的链码才能创建一个新的分支来衍生出孙密钥。
-
父私钥-> 子私钥-> 子公钥
- 父公钥-> 子公钥
扩展密钥
将密钥以及链码这两个重要的部分组合在一起,称为扩展密钥(extended key)。术语“扩展密钥”也被认为是“可扩展的密钥”,因为这种密钥可以用来派生子密钥。扩展密钥可以简单地表示为将密钥与链码串联成的序列并储存。
-
两种类型的扩展密钥:
- 私钥以及链码组成扩展私钥,前缀为 xprv,它可用来派生子私钥(子私钥可以用来派生子公钥)。
- 公钥以及链码组成扩展公钥,前缀为 xpub,它可以用来派生子公钥
-
扩展公钥及其应用:
- 部署扩展公钥可以创造出无限数量的公钥以及比特币地址,但是不能花费发送到这个地址里的任何比特币。与此同时,在另一种更安全的服务器上,扩展私钥可以衍生出对应的私钥,签署交易支付花费。
- 应用一:安装扩展公钥在电商的 web 服务器上。web 服务器可以使用公钥衍生函数去给每一笔交易(比如客户的购物车)创造一个新的比特币地址。服务器没有私钥,也就避免了被盗的风险。
- 应用二:冷存储或者硬件钱包。在这种情况下,扩展私钥可以储存在纸钱包或者硬件设备中,与此同时扩展公钥可以在线保存。用户可以任意创建“收款”地址,而私钥可以安全地在离线状态下保存。为了使用资金,用户可以用扩展私钥在比特币钱包中进行离线签名或者通过硬件钱包设备签名交易。
-
存在的安全隐患
如果知道某个子私钥,有可能造成父私钥泄露,推导如下:
- 两种子密钥的派生方式的输入参数均为父公钥,父链码,父索引号,导致 512bits 输出一致,链码(右 256bits)也一致。
- 因为扩展公钥包含链码以及可生成后代链码,可得左 256bits。
- 根据子私钥 = 左 256bits + 父私钥,如果相应子私钥泄露,则可逆推得到父私钥。
- 继续逆推最终可得到已知最早的扩展公钥所对应的私钥。
子私钥强派生
这个强派生函数使用了父私钥去推导子私钥。这导致 512bits 输出以及链码(右 256bits)与派生子公钥方式生成的不一致。因此无法获得相应的左 256bits,也就无法倒推出父私钥。
- 注意:这样也会导致两种方式生成的子公钥也不相同。如果后续使用扩展公钥的方式作为收款地址,后续派生子私钥的方式需要为正常派生,但就算后续发生私钥泄露,最多只会影响到这一分支。
因此强派生也因此被用于密钥树中扩展公钥的上一层以创造“间隙/防火墙”。
为了避免主密钥泄露,主密钥所衍生的第一层级的子密钥总是通过强化衍生得来。
索引码、密钥识别符、路径
-
索引码
- 常规派生索引码在 0 和 $2^{31}-1$(0x0 到 0x7FFFFFFF)之间。
- 强派生索引码在 $2{31}$和$2{32}-1$(0x80000000 到 0xFFFFFFFF)之间。
- 为了让索引码更容易被阅读和显示,强派生的索引号码也是从 0 开始展示的,但是右上角有一个小撇号。第一个常规子私钥因此被表述为 0,但是第一个强化子私钥(索引号为 0x80000000)就被表示为 0'。
-
HD 钱包密钥识别符(路径)
- 每个级别之间用斜杠(/)字符来表示。
- 由主私钥派生出的私钥起始以“m”打头。由主公钥派生的公钥起始以“M”打头。
HD path 密钥描述 m/0 主私钥(m)衍生的第一个子私钥(0) m/0/0 第一个子私钥 (m/0)衍生的第一个子私钥(0) m/0'/0 第一个强化子私钥 (m/0')衍生的第一个常规子私钥(0) m/1/0 第二个子私钥 (m/1)衍生的第一个子私钥(0) M/23/17/0/0 第 24 个子公钥(M/23)衍生的第 18 个子公钥(M/23/17)的第一个子公钥(M/23/17/0)的第一个子公钥(0) -
HD 钱包树状结构的导航
BIP-44 指定了包含 5 个预定义树状层级的结构:
m / purpose' / coin_type' / account' / change / address_index
- 第 1 层的 purpose 总是被设定为 44'。
- 第 2 层的“coin_type”特指币种,允许多货币 HD 钱包中的货币在第二个层级下有自己的子树结构。
- 第 3 层是“account”,这允许用户创建多个独立的子账号,便于财务统计或者部门管理。
- 第 4 层是“change”。每一个 HD 钱包在这一层都有两个子树,一个用来创建收款地址,另外一个用来创建找零地址。注意无论先前的层级是否使用强派生,这一层级使用的都是常规派生。这是为了允许这一层级的树可以通过使用扩展公钥的方式生成下一级的公钥。
- 被 HD 钱包派生的可用地址是第 4 层级的子级,就是第 5 层级的“address_index”。
HD path Key described M/44'/0'/0'/0/2 比特币主账户的第三个收款公钥 M/44'/0'/3'/1/14 比特币第四个账户的第十五个找零收款公钥 m/44'/2'/0'/0/1 Litecoin 主账户中的第二个私钥,用于签名交易
文章来源: 博客园
- 还没有人评论,欢迎说说您的想法!