精力有限,后期修订以github为主,建议大家移步github链接阅读更新版本,感谢理解:
https://github.com/tianmingyun/MasterBitcoin2CN
本文约5300字。
5.3从种子中创造HD钱包
HD钱包从单个root seed中创建,为128到256位的随机数。最常见的是,这个种子是从助记符产生的,如上一节所述。
HD钱包的所有的确定性都衍生自这个根种子。任何兼容HD 钱包的根种子也可重新创造整个HD钱包。所以简单的转移HD钱包的根种子就让HD钱包中所包含的成千上百万的密钥 被复制,储存导出以及导入。
创建主密钥以及HD钱包的主链代码的过程如下图所示。
根种子输入到HMAC-SHA512算法中就可以得到一个可用来创造master private key(m) 和 a master chain code的哈 希。主私钥(m)之后可以通过使用我们在本章先前看到的那个普通椭圆曲线m * G过程生来成相对应的主公钥(M)。 链代码可以给从母密钥中创造子密钥的那个方程中引入的熵。如下一节所示。
5.3.1私有子密钥的衍生
分层确定性钱包使用CKD(child key derivation)方程去从母密钥衍生出子密钥。
子密钥衍生方程是基于单项哈希方程。这个方程结合了:
一个母私钥或者公共钥匙(ECDSA未压缩键)
一个叫做链码(256 bits)的种子
一个索引号(32 bits)
链码是用来给这个过程引入看似的随机数据的,使得索引不能充分衍生其他的子密钥。因此,有了子密钥并不能让它发 现自己的相似子密钥,除非你已经有了链码。最初的链码种子(在密码树的根部)是用随机数据构成的,随后链码从各 自的母链码中衍生出来。
这三个项目(母私钥,链码,索引)相结合并散列可以生成子密钥,如下。
母公共钥匙——链码——以及索引号合并在一起并且用HMAC-SHA512方程散列之后可以产生512位的散列。所得的散 列可被拆分为两部分。散列右半部分的256位产出可以给子链当链码。左半部分256位散列以及索引码被加载在母私钥上来衍生子私钥。在图5-10中,我们看到这个说明——索引集被设为0去生产母密钥的第0个子密钥(第一个通过索 引)。
改变索引可以让我们延长母密钥以及创造序列中的其他子密钥。比如子0,子1,子2等等。每一个母密钥可以有2,147,483,647 (2^31) 个子密钥。231是整个232范围可用的一半,因为另一半是为特定类型的推导而保留的,我们将在本章稍后讨论。
向密码树下一层重复这个过程,每个子密钥可以依次成为母密钥继续创造它自己的子密钥,直到无限代。
5.3.2使用衍生的子密钥
子私钥不能从非确定性(随机)密钥中被区分出来。因为衍生方程是单向方程,所以子密钥不能被用来发现他们的母密 钥。子密钥也不能用来发现他们的相同层级的姊妹密钥。如果你有第n个子密钥,你不能发现它前面的(第n-1)或者 后面的子密钥(n+1)或者在同一顺序中的其他子密钥。只有母密钥以及链码才能得到所有的子密钥。没有子链码的 话,子密钥也不能用来衍生出任何孙密钥。你需要同时有子密钥以及对应的链码才能创建一个新的分支来衍生出孙密 钥。
那子私钥自己可被用做什么呢?它可以用来做公共钥匙和比特币地址。之后它就可以被用在那个地址来签署交易和支付任 何东西。
提示子密钥、对应的公共钥匙以及比特币地址都不能从随机创造的密钥和地址中被区分出来。事实是它们所在的序 列,在创造他们的HD钱包方程之外是不可见的。一旦被创造出来,它们就和“正常”钥匙一样运行了。
5.3.3扩展密钥
正如我们之前看到的,密钥衍生方程可以被用来创造钥匙树上任何层级的子密钥。这只需要三个输入量:一个密钥,一 个链码以及想要的子密钥的索引。密钥以及链码这两个重要的部分被结合之后,就叫做extended key。术语“extended key”也被认为是“可扩展的密钥”是因为这种密钥可以用来衍生子密钥。
扩展密钥可以简单地被储存并且表示为简单的将256位密钥与256位链码所并联的512位序列。有两种扩展密钥。扩展的 私钥是私钥以及链码的结合。它可被用来衍生子私钥(子私钥可以衍生子公共密钥)公共钥匙以及链码组成扩展公共钥 匙。这个钥匙可以用来扩展子公共钥匙,见“生成公钥”章节。
想象一个扩展密钥作为HD钱包中钥匙树结构的一个分支的根。你可以衍生出这个分支的剩下所有部分。扩展私人钥匙 可以创建一个完整的分支而扩展公共钥匙只能够创造一个公共钥匙的分支。
提示一个扩展钥匙包括一个私钥(或者公共钥匙)以及一个链码。一个扩展密钥可以创造出子密钥并且能创造出在钥 匙树结构中的整个分支。分享扩展钥匙就可以访问整个分支。
扩展密钥通过Base58Check来编码,从而能轻易地在不同的BIP0032-兼容钱包之间导入导出。扩展密钥编码用的 Base58Check使用特殊的版本号,这导致在Base58编码字符中,出现前缀“xprv”和“xpub”。这种前缀可以让编码更易被 识别。因为扩展密钥是512或者513位,所以它比我们之前所看到的Base58Check-encoded串更长一些。
这是一个在Base58Check中编码的扩展私钥的例子:
xprv9tyUQV64JT5qs3RSTJkXCWKMyUgoQp7F3hA1xzG6ZGu6u6Q9VMNjGr67Lctvy5P8oyaYAL9CAWrUE9i6GoNMKUga5biW6Hx4tws2six3b9c
这是在Base58Check中编码的对应的扩展公共钥匙:
xpub67xpozcx8pe95XVuZLHXZeG6XWXHpGq6Qv5cmNfi7cS5mtjJ2tgypeQbBs2UAR6KECeeMVKZBPLrtJunSDMstweyLXhRgPxdp14sk9tJPW9
5.3.4公共子钥匙推导
正如之前提到的,分层确定性钱包的一个很有用的特点就是可以不通过私钥而直接从公共母钥匙派生出公共子钥匙的能 力。这就给了我们两种去衍生子公共钥匙的方法:或者通过子私钥,再或者就是直接通过母公共钥匙。
因此,扩展的公共钥匙可以在HD钱包结构的分支中,被用来衍生所有的公钥(且只有公共钥匙)。
这种快捷方式可以用来创造非常保密的public-key-only配置。在配置中,服务器或者应用程序不管有没有私钥,都可以 有扩展公共钥匙的副本。这种配置可以创造出无限数量的公共钥匙以及比特币地址。但是不可以花送到这个地址里的任 何比特币。与此同时,在另一种更保险的服务器上,扩展私钥可以衍生出所有的对应的可签署交易以及花钱的私钥。
这种方案的一个常见的方案是安装一个扩展的公共钥匙在服务电商公共程序的网络服务器上。网络服务器可以使用这个 公共钥匙衍生方程去给每一笔交易(比如客户的购物车)创造一个新的比特币地址。但为了避免被偷,网络服务商不会 有任何私钥。没有HD钱包的话,唯一的方法就是在不同的安全服务器上创造成千上万个比特币地址,之后就提前上传 到电商服务器上。这种方法比较繁琐而且要求持续的维护来确保电商服务器不“用光”公共钥匙。
这种解决方案的另一种常见的应用是冷藏或者硬件钱包。在这种情况下,扩展的私钥可以被储存在纸质钱包中或者硬件 设备中(比如 Trezor 硬件钱包)与此同时扩展公共钥匙可以在线保存。使用者可以根据意愿创造“接收”地址而私钥可以 安全地在线下被保存。为了支付资金,使用者可以使用扩展的私钥离线签署比特币客户或者通过硬件钱包设备(比如 Trezor)签署交易。图4-12阐述了扩展母公共钥匙来衍生子公共钥匙的传递机制。
图5-11扩展母公共钥匙来创造一个子公共钥匙
图5-11扩展母公共钥匙来创造一个子公共钥匙
5.3.5在网店中使用扩展公钥(xpub)
让我们看看如何使用HD钱包继续我们的故事与Gabriel的网上商店。
Gabriel在一个网络上的托管服务器上建立一个简单的WordPress页面,作为他的网上商店。他的商店是相当基本的只有几个页面和一个单一的比特币地址的订单。
Gabriel使用他的Trezor设备生成的第一个比特币地址作为他的商店的主要比特币地址。这样,所有收到的付款都将支付给他的Trezor硬件钱包所控制的地址。
客户可以使用表格提交订单,并向Gabriel发布的比特币地址发送付款,触发一封电子邮件,其中包含Gabriel的订单详细信息。每周只需要几个订单,这个系统运行得很好。
然而,这个小型网络商店变得相当成功,并吸引了很多来自当地社区的订单。Gabriel很快就被淹没了。由于所有订单都支付相同的地址,因此很难正确匹配订单和交易,特别是当同一数量的多个订单紧密相连时。
Gabriel的HD钱包通过在不知道私钥的情况下获取公共子密钥的能力,提供了更好的解决方案。 Gabriel可以在他的网站上加载一个扩展的公开密钥(xpub),这可以用于为每个客户订单导出唯一的地址。Gabriel可以花费他的Trezor的资金,但加载在网站上的xpub只能生成地址并收到资金。HD钱包的这个功能是一个很好的安全功能。 Gabriel的网站不包含任何私钥,因此不需要高级别的安全性。
为了导出xpub,Gabriel将使用基于Web的软件与Trezor硬件钱包配合使用。必须插入Trezor设备才能导出公钥。请注意,硬件钱包永远不会导出私钥,这些密钥始终保留在设备上。下图显示了Gabriel用于导出xpub的Web界面。
图5-12从Trezor硬件钱包导出xpubGabriel将xpub复制到他的网店的比特币店软件。 他使用Mycelium Gear,它是一个用于各种网络托管和内容平台的开放源代码网站插件。 Mycelium Gear使用xpub为每次购买生成一个唯一的地址。
5.3.6硬化子密钥的衍生
从扩展公共钥匙衍生一个分支公共钥匙的能力是很重要的,但牵扯一些风险。访问扩展公共钥匙并不能得到访问子私人 密钥的途径。但是,因为扩展公共钥匙包含有链码,如果子私钥被知道或者被泄漏的话,链码就可以被用来衍生所有的 其他子私钥。一个简单地泄露的私钥以及一个母链码,可以暴露所有的子密钥。更糟糕的是,子私钥与母链码可以用来 推断母私钥。
为了应对这种风险,HD钱包使用一种叫做hardened derivation的替代衍生方程。这就“打破”了母公共钥匙以及子链码之 间的关系。这个硬化衍生方程使用了母私钥去推导子链码,而不是母公共钥匙。这就在母/子顺序中创造了一道“防火墙”——有链码但并不能够用来推算子链码或者姊妹私钥。强化的衍生方程看起来几乎与一般的衍生的子私钥相同,不同的是母私钥被用来输入散列方程中而不是母公共钥匙,如图4-13所示。
当强化私钥衍生函数被使用时,得到的子私钥以及链码与使用一般衍生方程所得到的结果完全不同的。得到的密钥“分 支”可以被用来生产不易被攻击的扩展公共钥匙,因为它所含的链码不能被用来开发或者暴露任何私钥。强化的衍生也因 此被用来在上一层级,使用扩展公共钥匙的的密钥树中创造“间隙”。
简单地来说,如果你想要利用扩展公共钥匙的便捷来衍生公共钥匙的分支而不将你自己暴露在泄露扩展链码的风险下, 你应该从强化母私钥,而不是一般的母私钥,来衍生公共钥匙。最好的方式是,为了避免了推到出主钥匙,主钥匙所衍 生的第一层级的子钥匙最好使用强化衍生。
5.3.7正常衍生和强化衍生的索引号码
用在衍生方程中的索引号码是32位的整数。为了区分密钥是从正常衍生方程中衍生出来还是从强化衍生方程中产出,这 个索引号被分为两个范围。索引号在0和2^31–1(0x0 to 0x7FFFFFFF)之间的是只被用在常规衍生。索引号在231和232– 1(0x80000000 to 0xFFFFFFFF)之间的只被用在强化衍生方程。因此,索引号小于2^31就意味着子密钥是常规的,而大 于或者等于2^31的子密钥就是强化型的。
为了让索引号码更容易被阅读和展示,强化子密码的索引号码是从0开始展示的,但是右上角有一个小撇号。第一个常 规子密钥因此被表述为0,但是第一个强化子密钥(索引号为0x80000000)就被表示为0'。第二个强化密钥依序有了索 引号0x80000001,且被显示为1',以此类推。当你看到HD钱包索引号i',这就意味着 2^31+i。
5.3.8HD钱包密钥识别符(路径)
HD钱包中的密钥是用“路径”命名的,且每个级别之间用斜杠(/)字符来表示(见表5-6)。由主私钥衍生出的私钥起始 以“m”打头。因此,第一个母密钥生成的子私钥是m/0。第一个公共钥匙是M/0。第一个子密钥的子密钥就是m/0/1,以 此类推。
密钥的“祖先”是从右向左读,直到你达到了衍生出的它的主密钥。举个例子,标识符m/x/y/z描述的是子密钥m/x/y的第z个 子密钥。而子密钥m/x/y又是m/x的第y个子密钥。m/x又是m的第x个子密钥。
5.3.9HD钱包树状结构的导航
HD钱包树状结构提供了极大的灵活性。每一个母扩展密钥有40亿个子密钥:20亿个常规子密钥和20亿个强化子密钥。 而每个子密钥又会有40亿个子密钥并且以此类推。只要你愿意,这个树结构可以无限类推到无穷代。但是,又由于有了 这个灵活性,对无限的树状结构进行导航就变得异常困难。尤其是对于在不同的HD钱包之间进行转移交易,因为内部 组织到内部分支以及亚分支的可能性是无穷的。
两个比特币改进建议(BIPs)提供了这个复杂问题的解决办法——通过创建几个HD钱包树的提议标准。BIP0043提出使 用第一个强化子索引作为特殊的标识符表示树状结构的“purpose”。基于BIP0043,HD钱包应该使用且只用第一层级的树的分支,而且有索引号码去识别结构并且有命名空间来定义剩余的树的目的地。举个例子,HD钱包只使用分支m/i'/是 为了表明那个被索引号“i”定义的特殊为目地。
在BIP0043标准下,为了延长的那个特殊规范,BIP0044提议了多账户结构作为“purpose”。所有遵循BIP0044的HD钱 包依据只使用树的第一个分支的要求而被定义:m/44'/。
BIP0044指定了包含5个预定义树状层级的结构:
m / purpose' / coin_type' / account' / change / address_index
第一层的purpose总是被设定为44'。
第二层的“coin_type”特指币种并且允许多元货币HD钱包中的货币在 第二个层级下有自己的亚树状结构。目前有三种货币被定义:Bitcoin is m/44'/0'、Bitcoin Testnet is m/44'/1',以及 Litecoin is m/44'/2'。
树的第三层级是“account”,这可以允许使用者为了会计或者组织目的,而去再细分他们的钱包到独立的逻辑性亚账户。 举个例子,一个HD钱包可能包含两个比特币“账户”:m/44'/0'/0' 和 m/44'/0'/1'。每个账户都是它自己亚树的根。
第四层级就是“change”。每一个HD钱包有两个亚树,一个是用来接收地址一个是用来创造找零地址。注意无论先前的 层级是否使用是否使用强化衍生,这一层级使用的都是常规衍生。这是为了允许这一层级的树可以在可供不安全环境 下,输出扩展的公共钥匙。
被HD钱包衍生的可用的地址是第四层级的子级,就是第五层级的树的“address_index”。比 如,第三个层级的主账户收到比特币支付的地址就是 M/44'/0'/0'/0/2。表5-7展示了更多的例子。
表5-7 BIP0044 HD 钱包结构的例子
本章完。
参考内容:
1、本文部分内容摘自《精通比特币》第一版中译本,特此说明并致谢。
我正在发起“和我一起阅读《精通比特币第二版》”活动。
我把这作为一次认知学习法的实践。
我希望将认知学习法与《精通比特币第二版》的研读结合起来,尝试总结出一套可行的区块链知识技能快速入门的方法。
这也将成为我们大家一起合作编写的一本书《认知学习比特币》的雏形(所有有价值的讨论都将成为这本书的素材)。
而且我更希望通过本次认知学习方法论的实践以及迭代,将这种经过实践的学习方法迁移到更多领域的学习中。
所以我们会有第二季,第三季......
欢迎扫描二维码加入。
欲知详情请扫描二维码
网友评论
大神,我想问一下,如何理解这句话?一般公钥是私钥推导出来,也就是说通过扩展公钥推导出来的公钥与扩展私钥推导出来的私钥然后生成的公钥是一样的吗?哪里可以看到这样的证明?只是椭圆曲线公钥算法支持还是所有的公钥算法支持?
有两种创建子公钥的方式, 方法一、是母私钥到子私钥,再到子公钥(英文版图5-10) 方法二、是从母私钥创建母公钥,后者加上链码和索引号再衍生子公钥。 (英文版图5-11) 但是你说的不同途径生成的子公钥是否相等,我之前没考虑过这个问题,后来在网上找了些资料。 经过与菜菜子老师和薛定谔的枪的同学的进一步讨论,我认可两种途径生成的子公钥应该是相等的这种说法。特别是下面链接中提到的公式: point( (父私钥 + i) % p ) == 父公钥 + point(i) == 子公钥 可以说明,只需要1个整数的index值,就可以唯一确定由密钥对衍生的子公钥。实际上是从两种途径走到一个结果。这样的结果还可以从不同途径进行验证。 http://www.qukuaiwang.com.cn/news/2132.html