区块链网站|NFTS 比特币地址 比特币 区块链第一应用(钥匙 地址 钱包)

比特币 区块链第一应用(钥匙 地址 钱包)

广告位

区块链的第一应用---比特币,之 (密钥、地址、钱包)

比特币的所有权是通过数字密钥、比特币地址和数字签名来建立的。数字密钥实际上并不存储在网络中,而是由用户生成并存储在一个文件或一个简单的数据库中,这个数据库称为钱包。存储在用户钱包中的数字密钥完全独立于比特币协议,可以由用户的钱包软件生成和管理,无需区块链或网络连接。Key实现了比特币很多有趣的特性,包括去中心化的信任和控制、所有权认证和基于密码证明的安全模型。

每笔比特币交易都需要在区块链中存储有效签名。只有有效的数字密钥才能产生有效的数字签名,所以拥有比特币的密钥副本就拥有了这个账户的比特币控制权。密钥成对出现,由一个私钥和一个公钥组成。公钥就像银行账户,而私钥就像控制账户或支票签名的PIN码。比特币用户很少直接看到数字密钥。一般存储在钱包文件中,由比特币钱包软件管理。

在比特币交易的支付过程中,收款人的公钥以其数字指纹表示,称为比特币地址,就像支票上支付对象的名字一样(即“收款人”)。一般来说,比特币地址是由一个公钥生成的,并与这个公钥相对应。但是,并不是所有的比特币地址都是公钥;它们也可以代表其他支付对象,比如脚本,我们将在本章后面提到。通过这种方式,比特币地址抽象了收款人,使得交易的目的地更加灵活,就像支票一样:这种支付工具可以支付到个人账户、公司账户、账单支付或现金支付。比特币地址是用户经常看到的密钥的唯一代表。他们只需要告诉别人比特币地址。

在本章中,我们将介绍钱包,它是钥匙所在的地方。我们将学习如何生成、存储和管理密钥。我们将回顾私钥和公钥、地址和脚本地址的各种编码格式。最后,我们将解释密钥的特殊用途:生成签名,证明所有权,以及创建比特币地址和纸质钱包。

4.1.1公钥加密和加密货币

公钥加密发明于20世纪70年代。它是计算机与信息安全的数学基础。

自从公钥加密发明以来,一些合适的数学函数被提出,如素数幂和椭圆曲线乘法。这些数学函数是不可逆的,也就是说,在一个方向上很容易计算,在相反的方向上却不可逆。基于这些数学函数的密码学使得生成数字密钥和不可伪造的数字签名成为可能。比特币使用椭圆曲线乘法作为其公钥加密的基本算法。

在比特币系统中,我们使用公钥加密来创建密钥对,用于控制比特币的获取。密钥对包括一个私钥和一个由其派生的唯一公钥。公钥用于接收比特币,私钥用于支付比特币时签署交易。

公钥和私钥之间的数学关系使得私钥能够用于生成特定消息的签名。这种签名可以验证公钥,而不会泄露私钥。

在支付比特币时,比特币的当前拥有者需要在交易中提交自己的公钥和签名(每笔交易的签名都不一样,但都是由同一个私钥生成的)。比特币网络中的每一个人都可以通过提交的公钥和签名来验证交易,确认交易是否有效,也就是确认付款人在那一刻拥有交易比特币的所有权。

为了方便起见,大多数比特币钱包工具都将私钥和公钥作为密钥对存储在一起。但是,可以从私钥计算出公钥,因此也可以只存储私钥。

4.1.2私钥和公钥

比特币钱包包含一系列密钥对,每个密钥对包括一个私钥和一个公钥。私钥(k)是一个数字,通常是随机选择的。有了私钥,我们可以通过使用椭圆曲线乘法的单向加密函数来生成公钥(k)。有了公钥(K),我们可以使用单向加密哈希函数来生成比特币地址(A)。本节我们将从生成私钥入手,讲述如何使用椭圆曲线运算从私钥生成公钥,最后从公钥生成比特币地址。私钥、公钥和比特币地址的关系如下图所示。

私钥

私钥只是一个随机选择的数字。对一个比特币地址内所有资金的控制取决于对相应私钥的所有权和控制权。在比特币交易中,私钥用于生成支付比特币所需的签名,以证明资金的所有权。私钥必须一直保密,因为一旦泄露给第三方,就相当于放弃了私钥保护的比特币。私钥也必须备份,防止意外丢失,因为私钥一旦丢失,就很难恢复,它保护的比特币也就永远丢失了。

比特币私钥只是一个数字。你可以用硬币、铅笔和纸随机生成你的私钥:将硬币翻转256次,用纸和笔记录正反面并转换成0和1。随机获得的256位二进制数可以作为比特币钱包的私钥。私钥可以进一步生成公钥。

从随机数生成私钥

生成密钥的第一步也是最重要的一步是找到一个足够安全的熵源,即随机源。生成一个比特币私钥,本质上和“选择1到2256之间的一个数”是一样的。只要选定的结果是不可预测的或不可重复的,具体的选号方法并不重要。比特币软件使用操作系统底层的随机数生成器生成256位熵(随机性)。通常情况下,操作系统的随机数生成器是由人工随机源初始化的,也可能需要摇动鼠标几秒钟才能初始化。对于真正的偏执狂,可以用掷骰子的方法,用铅笔和纸记录下来。

更准确地说,私钥可以是1到n-1之间的任意数,其中n是常数(n=1.158*1077,略小于2256),由比特币使用的椭圆曲线的阶来定义(参见4.1.5椭圆曲线密码学解释)。为了生成这样的私钥,我们随机选择一个256位的数,并检查它是否小于n-1。从编程的角度来看,一般是通过从一个密码安全的随机源中取出一长串随机字节,用SHA256哈希算法进行计算,这样就可以方便地生成一个256位的数。如果运算结果小于n-1,我们有一个合适的私钥。否则,我们将使用另一个随机数再次重复。

本书强烈建议读者不要使用自己的代码或编程语言内置的简单随机数生成器来获取随机数。我们建议读者使用加密安全的伪随机数发生器(CSPRNG),并需要有一个来自具有足够熵的源的种子。在使用随机数生成器的库时,有必要仔细研究它的文档,以确保它是加密的和安全的。CSPRNG的正确实现是密钥安全性的关键。

以下是随机生成的私钥(K),以十六进制格式表示(256位二进制数,以64位十六进制数显示,每个十六进制数占用4位):

1e 99423 a4 ed 27608 a 15 a 2616 a2 b 0 e 9e 52 ced 330 AC 530 edcc 32 c8 ffc 6a 526 aedd

比特币私钥空间的大小是2256,这是一个非常大的数字。用十进制表示,大约是1077,而可见宇宙估计只有1080个原子。

要使用比特币核心客户端生成新密钥(见第3章),可以使用getnewaddress命令。出于安全原因,命令运行后,只显示生成的公钥,而不显示私钥。如果希望bitcoind显示私钥,可以使用dumpprivkey命令。dumpprivkey命令将以Base58校验和编码的格式显示私钥,这种格式称为钱包导入格式(WIF),在“私钥格式”一节中有详细说明。以下是使用这两个命令生成和显示私钥的示例:

$ bitcoind getnewaddress 1j 7 MDG 5 rbqyuhenydx 39 wvwk 7 fslpeoxzy $ bitcoind dump privkey 1j 7 MDG 5 rbqyuhenydx 39 wvwk 7 fslpeoxzykxfc 1 jmww coacicawz 3 exa 96 MBM 6 TB 3 tyzgm f 6 ywgdgwzgawvrtjdumprivkey命令只是读取钱包中getnewaddress命令生成的私钥并显示出来。Bitcoind不知道私钥和公钥。除非密钥对存储在wallet中,否则dumpprivkey命令是有效的。

dumpprivkey命令无法从公钥中获取对应的私钥,因为这是不可能的。该命令只提取wallet中现有的私钥,即getnewaddress命令生成的私钥。

您也可以使用命令行sx工具(参见“3.3.1 Libbitcoin和sx工具”)通过newkey命令生成并显示私钥:

$ sx新密钥5j 3 mbba h 58 cpq 3y 5 rnjpukpe 62 sq 5 tfc vu 2 jpbnkehfsyb 1 jcn 4 . 1 . 4公钥

公钥可以通过椭圆曲线乘法由私钥计算出来,这是一个不可逆的过程:k=k * g .其中k是私钥,g是一个称为生成点的常数点,k是得到的公钥。它的逆运算叫做“求离散对数”——,从已知的公钥K中很难找到私钥k——,就像尝试K的所有可能值一样,也就是蛮力搜索。在演示如何从私钥生成公钥之前,让我们稍微详细地了解一下椭圆曲线密码学。

4.1.5椭圆曲线的加密解释

椭圆曲线加密是一种基于离散对数问题的非对称(或公钥)加密方法,可以用椭圆曲线上的点相加或相乘来表示。

上图是椭圆曲线的一个例子,类似于比特币中使用的曲线。

比特币使用一种特殊的椭圆曲线和一系列由secp256k1标准定义的数学常数。本标准由美国国家标准与技术研究所(NIST)制定。Secp256k1曲线由以下函数定义,它可以产生一条椭圆曲线:

y2=(x3 ^ 7)}除以(Fp)

或者

y2模p=(x3 7)模p

上面的mod p(素数p模)表示曲线在素数p的有限域上,也写作Fp,其中p=225623229282726241,这是一个非常大的素数。

由于这条曲线是定义在素数阶的有限域上,而不是实数范围内,所以它的函数图像看起来就像是散布在二维中的散点图,很难画出和表示。但数学原理类似于实数范围内的椭圆曲线。例如,下图显示了有限域中的一条椭圆曲线,其素数阶为17,以网格上一系列分散点的形式出现。secp256k1的比特币椭圆曲线可以想象成一个巨大网格上一系列更复杂的散乱点。

图:椭圆曲线上的椭圆曲线密码学F(p),其中p=17

举个例子,这是secp256k1曲线上的点P,它的坐标是(x,y)。你可以用Python检查一下:

p=(55066263022277343669578718895168534326250634537759417550187360389116729240,32670510083085130507043454472729240。p=115792089237316195423570985008687984665640564039457584079834671663 x=55066263022773436 695787188951685343262506033。在计算机中,有时表示为X=Y=0(虽然这不满足椭圆曲线方程,但可以作为特例来查)。还有一种算符,叫“加法”,就像小学数学里实数的加法一样。给定椭圆曲线上的两点P1和P2,椭圆曲线上一定有第三点P3=P1P2。

在几何学中,这第三点P3可以通过在P1和P2之间画一条线来确定。这条直线正好与椭圆曲线上的一点相交。这个点就是P3的=(x,y)。然后,在x轴上做映射,得到P3=(x,-y)。

下面是几个特例,可以说明“无穷远点”存在的必要性。如果P1和P2是同一点,P1和P2之间的直线就是P1点的切线。曲线上只有一个新点与切线相交。切线的斜率可以通过微分得到。即使将曲线点限制在两个整数坐标上,也可以得到斜率!

在某些情况下(即,如果P1和P2具有相同的x值但不同的y值),切线将完全垂直。在这种情况下,P3=“无穷远点”。

如果P1是“无限点”,那么它的总和P1P2=P2。同样,当P2是一个无穷远点时,那么P1P2=P1。这就是使无穷远点类似于0的作用。

原来这里算符服从结合律,意思是(A B)C=A(B C)。也就是说,我们可以不加括号直接写一个B C,不会混淆。

至此,我们已经定义了椭圆加法。对于扩展加法,我们先定义乘法为标准。给定椭圆曲线上的点P,若k为整数,则kP=P P P … P(k次)。注意,K有时会被混淆,称为“指数”。

生成一个公钥

以随机生成的私钥K为起点,乘以曲线上定义的生成点G,得到曲线上的另一个点,即对应的公钥K .生成点是secp256k1标准的一部分,比特币密钥的生成点都是一样的:

K=1e 99423 a4 ed 27608 a 15A 2616 a2 b 0 e 9 e 52 ced 330 AC 530 edcc 32 c8 ffc 6a 526 aedd * G其中K是私钥,G是生成点,在这条曲线上得到的点K就是公钥。因为所有比特币用户的生成点都是一样的,一个私钥K乘以G会得到相同的公钥K,K和K的关系是固定的,但是只能进行一个方向的运算,即从K可以得到K.这就是为什么你可以和任何人分享比特币地址(K的衍生物)而不泄露私钥(K)。

因为数学运算是单向的,私钥可以转换成公钥,但公钥不能转换回私钥。

为了实现椭圆曲线乘法,我们将生成的私钥K和生成的点G相乘以获得公钥K:

K=1e 99423 a4 ed 27608 a 15A 2616 a2 b 0 e 9 e 52 ced 330 AC 530 edcc 32 c8 ffc 6a 526 aedd * G公钥K定义为点K=(x,y):

K=(x,y),其中x=f 028892 bad 7 ed 57d 2 FB 57 BF 33081d 5 cfcf 6 f 9 ed 3d 7 f 159 C2 e 2 ff 579 DC 341 ay=07 cf 33 da 18 BD 734 c 600 b 96 a 72 BC 4749d 5141 c 90 EC 8 AC 328 AE 52 ddf e2e 505 BDB为了记忆,数学原理是一样的。我们的目标是求生成点g的倍数kG,也就是把g加k次。在椭圆曲线中,点的相加相当于从该点画一条切线寻找另一个与曲线相交的点,然后映射到X轴。

上图是在曲线上得到G,2G,4G的几何运算。

大部分比特币程序使用OpenSSL加密库进行椭圆曲线计算。例如,可以通过调用EC_POINT_mul()函数来计算公钥。

4.2比特币地址

比特币地址是一串数字和字母,可以分享给任何想给你比特币的人。公钥(也是由数字和字母组成的字符串)生成的比特币地址以数字“1”开头。以下是一个比特币地址的例子:

17 MDG 5 rbqyuhenydx 39 wvwk 7 fslpeoxzy在交易中,比特币地址通常以收款方的身份出现。如果把一笔比特币交易比作一张支票,比特币地址就是收款方,这也是我们要写在收款方一栏的内容。支票的收款人可以是银行账户、公司、机构,甚至是现金支票。支票不需要指定具体的账户,而是使用一个通用名称作为收款人,这使其成为一种相当灵活的支付工具。同样,比特币地址的使用使得比特币交易变得灵活。比特币地址可以代表一对公钥和私钥的所有者,也可以代表其他东西,比如将在第132页“P2SH (Pay-to-Script-Hash)”一节中讨论的支付脚本。现在,让我们看一个从公钥生成比特币地址的简单例子。

比特币地址可以通过单向加密哈希算法从公钥中获得。哈希算法是一个单向函数,它接收任意长度的输入来生成指纹摘要。加密函数在比特币中应用广泛:比特币地址、脚本地址、挖掘中的工作量证明算法。用于从公钥生成比特币地址的算法有安全哈希算法(SHA)和竞争完整性原语评估消息摘要(RIPEMD),尤其是SHA256和RIPEMD160。

以公钥K为输入,计算其SHA256哈希值,并基于此结果计算RIPEMD160哈希值,得到一个长度为160位(20字节)的数:

在公式A=RIPEMD160(SHA256(K))中,K是公钥,A是生成的比特币地址。

比特币地址不同于公钥。比特币地址是由公钥的单向哈希函数生成的。

用户通常看到的比特币地址都是用“Base58Check”编码的(参见第72页“Base58和Base58Check编码”一节)。这种编码使用58个字符(Base58数字系统)和校验码,提高了可读性,避免了歧义,并有效地防止了地址转录和输入中的错误。base 58校验码还用于比特币的其他地方,如比特币地址、私钥、加密密钥、脚本哈希等,提高输入的可读性和准确性。在下一节中,我们将详细解释Base58Check的编码机制,以及它产生的结果。下图描述了如何从公钥生成比特币地址。

4.2.1 Base58和Base58校验码

为了更简洁、更方便地表达一长串数字,许多计算机系统会使用由数字和字母组成的比十进制大的记数法。例如,传统的十进制计数系统使用从0到9的十个数字,而十六进制系统使用六个额外的字母A-F,同样一个数,它的十六进制表示会比十进制表示短。此外,Base64使用26个小写字母、26个大写字母、10个数字和两个符号(如“”和“/”)在基于文本的媒体(如电子邮件)中传输二进制数据。Base64通常用于对邮件中的附件进行编码。Base58是一种基于文本的二进制编码格式,用于比特币和其他加密货币。这种编码格式不仅实现了数据压缩,保持了易读性,还具有错误诊断的功能。Base58是Base64编码格式的子集,同样使用大小写字母和10个数字,但舍弃了某些字体中容易读错和混淆的一些字符。具体来说,Base58不包含0(数字0)、O(大写字母O)、L(小写字母L)、I(大写字母I)以及Base64中的字符“”和“/”。简而言之,Base58由大小写字母和数字组成,不包括(0,o,l,I)。

例4-1比特币的Base58字母表

13456789 abcdefghjklmnpqrstuvwxyzabcdefghijkmnopkrsttuvwxyzbase 58 check是比特币常用的base 58编码格式,增加了错误校验码,用于检查数据转录中的错误。校验码为4字节长,添加到要编码的数据中。校验码是从要编码的数据的哈希值中获得的,因此可以用来检测和避免转录和输入中的错误。使用Base58check编码格式时,编码软件会计算原始数据的校验码,并与结果数据中的校验码进行比较。两者不匹配说明有错误,所以Base58Check格式的数据无效。比如一个错误的比特币地址不会被钱包认为是有效地址,否则这个错误会造成资金损失。

为了使用Base58Check编码格式对数据(数字)进行编码,我们首先为数据添加一个名为“版本字节”的前缀,用于指定要编码的数据类型。比如比特币地址的前缀是0(十六进制0x00),而私钥的前缀是128(十六进制0x80)。表4-1列出了前缀的一些常见版本。

接下来,我们计算“双重哈希”校验码,这意味着我们必须对之前的结果(前缀和数据)运行两次SHA256哈希算法:

校验和=sha256 (sha256(前缀数据))在生成的32个字节的哈希值中(两次哈希运算),我们只取前4个字节。这4个字节用作校验码。将校验码添加到数据中。

结果由三部分组成:前缀、数据和校验码。这个结果由前面描述的Base58字母表编码。下图描述了Base58Check的编码过程。

base 58校验码:一个base 58格式,一个版本,一个验证过的格式,可以清晰的对比特币数据进行编码。

在比特币中,大部分需要展示给用户的数据都是用Base58Check编码的,可以实现数据压缩、可读性和错误检查。Base58Check编码中的版本前缀是数据的格式容易区分,编码后的数据头包含确定的属性。这些属性使用户很容易知道编码数据的类型以及如何使用它们。例如,我们可以看到它们之间的区别。Base58Check编码的比特币地址以1开头,Base58Check编码的私钥WIF以5开头。表4-1显示了一些版本前缀及其对应的Base58格式。

表4-1 base 58的前缀检查版本和编码结果

类型前缀(十六进制)Base58格式比特币地址0x053支付到脚本哈希地址0x053比特币测试网地址0x6fm或n私钥wif0x805,K或LBIP38加密私钥0x01426PBIP32扩展公钥0x0488B21Expub

我们回顾一下比特币地址生成的全过程,从私钥,到公钥(椭圆曲线上的一点),再到经过两次哈希的地址,最后生成Base58Check格式的比特币地址。例4-2的C代码完整详细地展示了从私钥到Base58Check编码的比特币地址的步骤。代码中使用了“3.3其他客户端、库、工具包”一节中介绍的libbitcoin库来实现一些辅助功能。

示例4-2从私钥生成以Base58Check格式编码的比特币地址

#include int main() { //私有密钥. BC:EC _ secret secret=BC:decode _ hash(\’ 038109007313 a 5807 b 2 ECC cc 082 c8 C3 fbb 988 a 973 ACF 1 a 7 df 9 ce 725 c 31 b 14776 \’);//获取公钥. BC:EC _ point public _ key=BC:secret _ to _ public _ key(秘密);\’ std:cout \’公钥:\’ BC:encode _ hex(Public _ key)STD:endl;//创建比特币地址。//正常情况下可以使用://BC:payment _ address payaddr;//bc:set_public_key(payaddr,public _ key);//const STD:string地址=pay addr。encoded();//计算P2PKH地址的公钥哈希const BC:short _ hash hash=BC:bit coin _ short _ hash(public _ key);BC:data _ chunk uncoded _ address;//保留25字节//[版本:1 ] //[哈希:20 ] //[校验和:4]未编码地址。储备(25);//版本字节,0是正常的BTC地址(P2PKH).未编码_地址。push _ back(0);//哈希数据bc:扩展数据(未编码地址,哈希);//校验和是通过对数据进行哈希运算,并从哈希中添加四个字节来计算的. bc:追加_校验和(未编码地址);//最后我们必须在比特币的base58编码维护中对结果进行编码(未编码_地址。size()==25);\’ const STD:string address=BC:encode _ base 58(uncoded _ address);\’\’ STD:cout \’ Address:\’ Address STD:endl;返回0;}正如编译并运行地址代码中展示的,由于代码使用预定义的私钥,所以每次运行都会产生相同的比特币地址。如例4-3所示。

例4-3 编译并运行地址代码

#编译addr.cpp代码$ g-o地址地址。CPP $(pkg-config-cflags-libs lib比特币)#运行地址可执行文件$。/addr公钥:0202 a 406624211 F2 abb DC 68 da 3d f 929 f 938 c 3399 DD 79 fac 1 b 51 b 0 E4 ad 1d 26 a 47 aa地址:1 pttajesdnovgne 6 ehc du 1 fpedx 7913 CK 4。2 .2密钥的格式

公钥和私钥的都可以有多种编码格式。一个密钥被不同的格式编码后,虽然结果看起来可能不同,但是密钥所编码数字并没有改变。这些不同的编码格式主要是用来方便人们无误地使用和识别密钥。

私钥的格式

私钥可以以许多不同的格式表示,所有这些都对应于相同的256位的数字。表4-2展示了私钥的三种常见格式。

表4-2 私钥表示法(编码格式)

种类版本描述己酮64十六进制数字wif 5 Base58检查编码:底座58,版本前缀为128,32位checksumWIF-compressedK或(尼日)拉各斯(Lagos的缩写)以上,编码前添加后缀0x01

表4-3展示了用这三种格式所生成的私钥。

表4-3 示例:同样的私钥,不同的格式

格式私钥hex 1e 99423 a4 ed 27608 a 15 a 2616 a2 b 0 e 9e 52 ced 330 AC 530 edcc 32 c8 ffc 6a 526 aed wif 5j 3 mbbah 58 cpq 3y rnjpukpe 62 sq 5 tfc vu 2 jpbnkeyhfsyb 1 jcnwif-compressed kx fc 1 jmwwcoacicawz 3 exa 96 MBM 63 tyz GMF 6 ywgdgwzgawvrtj

这些表示法都是用来表示相同的数字、相同的私钥的不同方法。虽然编码后的字符串看起来不同,但不同的格式彼此之间可以很容易地相互转换。

将Base58Check编码解码为十六进制

sx工具包(参见\”3.3.1利比亚比特币和sx工具\”)可用来编写一些操作比特币密钥、地址及交易的壳脚本和命令行\”管道\”。你也可以使用sx工具从命令行对Base58Check格式进行解码。

我们使用的命令是base58check校验解码:

$ sx base 58校验解码5j 3 mbbah 58 cpq 3y 5 rnjpukpe 62 sq 5 tfc vu 2 jpbnkeyhfsyb 1 jcn 1 e 99423 a4 ed 27608 a15 a 2616 a2 b 0 e 9 e 52 ced 330 AC 530 edcc 32 c8 ffc 6 a 526 aedd 128所得结果是十六进制的密钥,紧接着是钱包导入格式(钱包导入格式,WIF)的版本前缀128。

将十六进制转换为Base58Check编码

要转换成Base58Check编码(和之前的命令正好相反),我们需提供十六进制的私钥和钱包导入格式(钱包导入格式,WIF)的版本号前缀128:

$ sx base 58 check-encode 1e 99423 a4 ed 27608 a 15 a 2616 a2 b 0 e 9 e 52 ced 330 AC 530 edcc 32 c8 ffc 6a 526 aedd 128 5j 3 mbbah 58 cpq 3y 5 rnjpukpe 62 sq 5 tfc vu 2 jpbnkeyhfsyb 1 jcn将十六进制(压缩格式密钥)转换为Base58Check编码

要将压缩的私钥编码为Base58Check(参见“压缩的私钥”一节),我们需要给十六进制私钥加上后缀01,然后使用与上面相同的方法:

K=04f 028892 bad 7 ed 57 D2 b 57 BF 33081 D5 CFF 6 F9 ed 3d 7f 159 C2 e 2 fff 579 DC 341 a 07 cf 333 da 18 BD 734 c 60 b 96 a 72 BBC 4749d 5141 c 90 E8 AC 328 AE 52 ddfe 505 BDB BDB生成的WIF压缩格式的私钥以字母“K”表示编码的私钥具有后缀“01”并且

公钥的格式

公钥也可以用许多不同的格式表示。最重要的是,它们可以分为两种形式:未压缩格式或压缩格式公钥。

从上一篇文章中我们知道,公钥是椭圆曲线上的一个点,由一对坐标(x,y)组成。公钥通常表示为前缀04后跟两个256位的数字。一个256位数是公钥的x坐标,另一个256位数是y坐标。前缀04用于区分未压缩的公钥,压缩的公钥以02或03开头。

下面是上一篇文章中私钥生成的公钥,其坐标x和y如下:

x=f 028892 bad 7 ed 57d 2 FB 57 BF 33081d 5 cfcf 6 f 9 ed 3d 7 f 159 C2 e 2 ff 579 DC 341 ay=07 cf 33 da 18 BD 734 c 600 b 96 a 72 BC 4749d 5141 c 90 EC 8 AC 328 AE 52 dfe 2 e 505 BDB这里是同一个520位数的公钥(130这个520位数以前缀04开头,后面是x

k=03f 028892 bad 7 ed 57 D2 b 57 BF 33081 D5 CFC F6 F9 ed 3d 7f 159 C2 e 2 fff 579 DC 341 a压缩公钥

压缩格式公钥的引入是为了减少比特币交易中的字节数,从而节省运行区块链数据库的节点的磁盘空间。大多数比特币交易都包含公钥,用于验证用户的凭据和支付比特币。每个公钥有520位(包括前缀、x坐标和y坐标)。如果每个块中有数百个事务,并且每天有数千个事务发生,则大量数据将被写入区块链。

正如我们在“4.1.4公钥”一节中看到的,公钥是椭圆曲线上的一个点(x,y)。椭圆曲线其实是一个数学方程,曲线上的点其实是方程的一个解。因此,如果我们知道公钥的x坐标,我们可以通过求解方程Y2Mod p=(X37) Mod p来获得y坐标,这种方案允许我们只存储公钥的x坐标,而省略y坐标,从而将公钥的大小和存储空间减少了256位。每次交换所需的字节数减少了近一半,日积月累大大节省了大量的数据传输和存储。

未压缩的公钥以04为前缀,而压缩的公钥以02或03为前缀。需要这两个不同的前缀,因为椭圆曲线加密公式的左侧是y2,这意味着Y的解来自平方根,平方根可以是正的,也可以是负的。更形象地说,Y坐标可以在X坐标轴的上方或下方。从图4-2中的椭圆曲线可以看出,曲线是对称的,从X轴看起来像两个对称的镜像边。因此,如果我们省略y坐标,我们必须存储y的符号(正或负)。换句话说,对于给定的x值,我们需要知道y值是在x轴之上还是之下,因为它们代表椭圆曲线上的不同点,即不同的公钥。当我们用二进制算术计算素数P的有限域上的椭圆曲线时,Y坐标可能是奇数,也可能是偶数,分别对应前面提到的Y值的正负符号。所以为了区分Y坐标的两个可能值,我们在生成压缩公钥的时候,如果Y是偶数,就用02作为前缀;如果y是奇数,使用03作为前缀。这样就可以根据公钥中给定的X值,正确推导出对应的Y坐标,将公钥解压缩成椭圆曲线上完整的点坐标。下图说明了公钥压缩:

以下是上一章生成的公钥,使用264位(66位十六进制数字)压缩公钥格式,其中前缀03表示Y坐标为奇数:

k=03f 028892 bad 7 ed 57 D2 b 57 BF 33081d 5 CFF 6 F9 ed 3d 7 f 159 C2 e 2 fff 579 DC 341 a这个压缩的公钥对应的是同一个私钥,也就是说它是由同一个私钥生成的。但是,压缩的公钥与未压缩的公钥有很大不同。更重要的是,如果我们使用双哈希函数(RIPEMD160(SHA256(K))将压缩后的公钥转换成比特币地址,得到的地址将与未压缩的公钥生成的地址不同。这个结果会比较混乱,因为一个私钥可以生成两种不同格式的公钥3354压缩格式和未压缩格式,这两种格式的公钥可以生成两个不同的比特币地址。但是,这两个不同比特币地址的私钥是一样的。

压缩公钥格式逐渐成为各种比特币客户端的默认格式。可以大大减少事务所需的字节数,同时也使得存储区块链所需的磁盘空间更小。然而,并不是所有的客户端都支持压缩公钥,所以那些支持压缩公钥的新客户端必须考虑如何处理来自不支持压缩公钥的旧客户端的事务。当一个钱包应用程序导入另一个钱包应用程序的私钥时,这变得尤其重要,因为新的钱包需要扫描区块链并找到与这些导入的私钥相关的所有交易。钱包应该扫描哪个比特币地址?新客户端不知道用哪个公钥:因为无论是压缩公钥生成的比特币地址,还是未压缩公钥生成的地址,都是合法的比特币地址,都可以用私钥正确签名,但却是完全不同的比特币地址。

为了解决这个问题,当私钥从钱包导出时,较新的比特币客户端将使用不同的钱包导入格式。这种新的钱包导入格式可以用来表示私钥已经用来生成压缩公钥,生成的比特币地址也是基于压缩公钥的。该方案可以解决导入的私钥来自旧钱包还是新钱包的问题,也可以解决公钥生成的比特币地址来自压缩公钥还是未压缩公钥的问题。最后,当新钱包扫描区块链时,它可以使用相应的比特币地址来查找该比特币地址在区块链的交易。我们将在下一节详细解释这种机制是如何工作的。

压缩格式私钥

实际上,“压缩私钥”是一个误导性的名称,因为当一个私钥以WIF压缩格式导出时,它不仅是未压缩的,而且比“未压缩私钥”长一个字节。这个额外的字节是私钥以01为后缀,表示私钥来自较新的钱包,只能用来生成压缩的公钥。私钥是未压缩的,不能被压缩。实际上,“压缩私钥”只是指“用于生成压缩公钥的私钥”,而“非压缩私钥”是指“用于生成非压缩公钥的私钥”。为了避免更多的误解,我们应该只说导出格式是“WIF压缩格式”或“WIF”,而不要说私钥是“压缩的”。

请注意,这些格式不可互换。在实现压缩公钥的较新的钱包中,私钥只能且总是导出为WIF压缩格式(前缀为K或L)。对于没有实现压缩公钥的老式钱包,私钥只能导出为WIF格式(前缀为5)。这样做的目的是给导入这些私钥的钱包一个信号:是使用压缩的公钥和比特币地址扫描区块链,还是使用未压缩的公钥和比特币地址。

如果一个比特币钱包实现了压缩格式公钥,那么它将会在所有交易中使用该压格式缩公钥。钱包中的私钥将会被用来生成压缩格式公钥,压缩格式公钥然后被用来生成交易中的比特币地址。当从一个实现了压缩格式公钥的比特币钱包导出私钥时,钱包导入格式(WIF)将会被修改为水不溶部分压缩格式,该格式将会在私钥的后面附加一个字节大小的后缀01。最终的Base58Check编码格式的私钥被称作WIF(“压缩\”)私钥,以字母\” K \”或\” L \”开头。而以\”5\”开头的是从较老的钱包中以WIF(非压缩)格式导出的私钥。

表4-4展示了同样的私钥使用不同的水不溶部分和水不溶部分压缩格式编码。

表4-4 示例:同样的私钥,不同的格式

格式私钥hex 1e 99423 a4 ed 27608 a 15a 2616 a2 b 0 e 9 e 52 ced 330 AC 530 edcc 32 c8 ffc 6a 526 aed wif 5j 3 mbbah 58 cpq 3y 5 rnjpukpe 62 sq 5 tfc vu 2 jpbnkeyhfsyb 1 jcn hex-compressed 1e 99423 a 4 ed 27608 a 15a 2616 a2 b 0 e 9 e 52 ced 330 AC 530 edcc 30

\”压缩格式私钥\”是一个不当用词!私钥不是压缩的106 .水不溶部分压缩格式的私钥只是用来表明他们只能被生成压缩的公钥和对应的比特币地址。相反地,“WIF压缩\”编码的私钥还多出一个字节,因为这种私钥多了后缀\”01\”。该后缀是用来区分\”非压缩格式\”私钥和\”压缩格式\”私钥。

4.3 用计算机编程语言实现密钥和比特币地址

最全面的比特币计算机编程语言库是维塔利克布特林写的pybitcointools。在例4-4中,我们使用pybitcointools库(导入为\”比特币\”)来生成和显示不同格式的密钥和比特币地址。

例4-4 使用pybitcointools库的密钥和比特币地址的生成和格式化过

导入比特币#生成随机私钥valid _ private _ key=False而not valid _ private _ key:private _ key=比特币。random _ key()decoded _ private _ key=比特币。decode_privkey(private_key,\’ hex \’)valid _ private _ key=0 decoded _ private _ key比特币\’ Nprint \’私钥(十六进制)为: \’,private_keyprint \’私钥(十进制)为: \’,解码的私有密钥#将私钥转换为水不溶部分格式wif _ encoded _ Private _ Key=比特币。\’ encode _ priv Key(decoded _ Private _ Key,\’ wif\’)print \’私钥(WIF)为: \’,wif_encoded_private_key#添加后缀\’ 01 \’以表示压缩的私钥compressed _ Private _ Key=Private _ Key \’ 01 \’ print \’私钥压缩(十六进制)为: \’,压缩私有密钥#的g,decoded_private_key)打印\’公钥(x,y)坐标为:\’,公共密钥#编码为十六进制,前缀04 hex _ encoded _ Public _ Key=比特币。\’ Encode _ pubkey(Public _ Key,\’ hex\’) print \’公钥(十六进制)为:\’,十六进制编码公共密钥#压缩公钥,根据y是偶数还是奇数调整前缀(公共密钥x,公共密钥y)=公共密钥if(公共密钥y % 2)==0:压缩前缀=\’ 02 \’ else例4-5显示了上段代码运行结果。

例4-5 运行密钥到地址-ecc-example.py

$ python密钥到地址-ECC-示例。巴拉圭私钥(十六进制)为:3 ABA 4162 c 7251 c 891207 b 747840551 a 71939 b 0 de 081 f 85 C4 e 44 cf 7c 13 e 41 da a6私钥(十进制)为:26563230048437957592

232553826663696440606756685920117476832299673293013768870Private Key (WIF) is: 5JG9hT3beGTJuUAmCQEmNaxAuMacCTfXuw1R3FCXig23RQHMr4KPrivate Key Compressed (hex) is: 3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa601Private Key (WIF-Compressed) is: KyBsPXxTuVD82av65KZkrGrWi5qLMah5SdNq6uftawDbgKa2wv6SPublic Key (x,y) coordinates is: (41637322786646325214887832269588396900663353932545912953362782457239403430124L, 16388935128781238405526710466724741593761085120864331449066658622400339362166L)Public Key (hex) is: 045c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ec↵243bcefdd4347074d44bd7356d6a53c495737dd96295e2a9374bf5f02ebfc176Compressed Public Key (hex) is: 025c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ecBitcoin Address (b58check) is: 1thMirt546nngXqyPEz532S8fLwbozud8Compressed Bitcoin Address (b58check) is: 14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3

例4-6是另外一个示例,使用的是Python ECDSA库来做椭圆曲线计算而非使用bitcoin的库。

例4-6 使用在比特币密钥中的椭圆曲线算法的脚本

import ecdsaimport randomfrom ecdsa.util import string_to_number, number_to_string# secp256k1, http://www.oid-info.com/get/1.3.132.0.10_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L_b = 0x0000000000000000000000000000000000000000000000000000000000000007L_a = 0x0000000000000000000000000000000000000000000000000000000000000000L_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8Lcurve_secp256k1 = ecdsa.ellipticcurve.CurveFp(_p, _a, _b)generator_secp256k1 = ecdsa.ellipticcurve.Point(curve_secp256k1, _Gx, _Gy, _r)oid_secp256k1 = (1, 3, 132, 0, 10)SECP256k1 = ecdsa.curves.Curve(\”SECP256k1\”, curve_secp256k1, generator_secp256k1,oid_secp256k1)ec_order = _rcurve = curve_secp256k1generator = generator_secp256k1def random_secret(): random_char = lambda: chr(random.randint(0, 255)) convert_to_int = lambda array: int(\”\”.join(array).encode(\”hex\”), 16) byte_array = [random_char() for i in range(32)] return convert_to_int(byte_array)def get_point_pubkey(point): if point.y() & 1: key = \’03\’ + \’%064x\’ % point.x() else: key = \’02\’ + \’%064x\’ % point.x() return key.decode(\’hex\’)def get_point_pubkey_uncompressed(point): key=\’04\’+\\ \’%064x\’ % point.x() + \\ \’%064x\’ % point.y() return key.decode(\’hex\’)# Generate a new private key.secret = random_secret() print \”Secret: \”, secret# Get the public key point.point = secret * generator print \”EC point:\”, pointprint \”BTC public key:\”, get_point_pubkey(point).encode(\”hex\”)# Given the point (x, y) we can create the object using:point1 = ecdsa.ellipticcurve.Point(curve, point.x(), point.y(), ec_order) assert point1 == point

例4-7显示了运行脚本的结果。

例4-7 安装Python ECDSA库,运行ec_math.py脚本

running the ec_math.py script$ # Install Python PIP package manager$ sudo apt-get install python-pip$ # Install the Python ECDSA library$ sudo pip install ecdsa$ # Run the script$ python ec-math.pySecret:38090835015954358862481132628887443905906204995912378278060168703580660294000EC point:(70048853531867179489857750497606966272382583471322935454624595540007269312627,105262206478686743191060800263479589329920209527285803935736021686045542353380)BTC public key: 029ade3effb0a67d5c8609850d797366af428f4a0d5194cb221d807770a15228734.4 比特币钱包

钱包是私钥的容器,通常通过有序文件或者简单的数据库实现。另外一种制作私钥的途径是 确定性密钥生成。在这里你可以用原先的私钥,通过单向哈希函数来生成每一个新的私钥,并将新生成的密钥按顺序连接。只要你可以重新创建这个序列,你只需要第一个私钥(称作种子、主私钥)来生成它们。在本节中,我们将会检查不同的私钥生成方法及其钱包结构。

比特币钱包只包含私钥而不是比特币。每一个用户有一个包含多个私钥的钱包。钱包中包含成对的私钥和公钥。用户用这些私钥来签名交易,从而证明它们拥有交易的输出(也就是其中的比特币)。比特币是以交易输出的形式来储存在区块链中(通常记为vout或txout)。

4.4.1 非确定性(随机)钱包

在最早的一批比特币客户端中,钱包只是随机生成的私钥集合。这种类型的钱包被称作零型非确定钱包。举个例子,比特币核心客户端预先生成100个随机私钥,从最开始就生成足够多的私钥并且每把钥匙只使用一次。这种类型的钱包有一个昵称“Just a Bunch Of Keys(一堆私钥)”简称JBOK。这种钱包现在正在被确定性钱包替换,因为它们难以管理、备份以及导入。随机钥匙的缺点就是如果你生成很多,你必须保存它们所有的副本。这就意味着这个钱包必须被经常性地备份。每一把钥匙都必须备份,否则一旦钱包不可访问时,钱包所控制的资金就付之东流。这种情况直接与避免地址重复使用的原则相冲突——每个比特币地址只能用一次交易。地址通过关联多重交易和对方的地址重复使用会减少隐私。0型非确定性钱包并不是钱包的好选择,尤其是当你不想重复使用地址而创造过多的私钥并且要保存它们。虽然比特币核心客户包含0型钱包,但比特币的核心开发者并不想鼓励大家使用。下图表示包含有松散结构的随机钥匙的集合的非确定性钱包。

4.4.2 确定性(种子)钱包

确定性,或者“种子”钱包包含通过使用单项离散方程而可从公共的种子生成的私钥。种子是随机生成的数字。这个数字也含有比如索引号码或者可生成私钥的“链码”(参见“4.4.4 分层确定性钱包(BIP0032/BIP0044)”一节)。在确定性钱包中,种子足够收回所有的已经产生的私钥,所以只用在初始创建时的一个简单备份就足以搞定。并且种子也足够让钱包输入或者输出。这就很容易允许使用者的私钥在钱包之间轻松转移输入。

4.4.3 助记码词汇

助记码词汇是英文单词序列代表(编码)用作种子对应所确定性钱包的随机数。单词的序列足以重新创建种子,并且从种子那里重新创造钱包以及所有私钥。在首次创建钱包时,带有助记码的,运行确定性钱包的钱包的应用程序将会向使用者展示一个12至24个词的顺序。单词的顺序就是钱包的备份。它也可以被用来恢复以及重新创造应用程序相同或者兼容的钱包的钥匙。助记码代码可以让使用者复制钱包更容易一些,因为它们相比较随机数字顺序来说,可以很容易地被读出来并且正确抄写。

助记码被定义在比特币的改进建议39中(参见\”附录2 比特币改进协议[bip0039]”),目前还处于草案状态。需注意的是,BIP0039是一个建议草案而不是标准方案。具体地来说,电子钱包和BIP0039使用不同的标准且对应不同组的词汇。Trezor钱包以及一些其他钱包使用BIP0039,但是BIP0039和电子钱包的运行不兼容。

BIP0039定义助记码和种子的创建过程如下:

1.创造一个128到256位的随机顺序(熵)。2.提出SHA256哈希前几位,就可以创造一个随机序列的校验和。3.把校验和加在随机顺序的后面。4.把顺序分解成11位的不同集合,并用这些集合去和一个预先已经定义的2048个单词字典做对应。5.生成一个12至24个词的助记码。

表4-5表示了熵数据的大小和助记码单词的长度之间的关系。

表4-5 助记码:熵及字段长度

熵(bits)校验符(bits)熵+校验符字段长128413212160516515192619818224723121256826424

助记码表示128至256位数。这可以通过使用私钥抻拉函数PBKDF2来导出更长的(512位)的种子。所得的种子可以用来创造一个确定性钱包以及其所派生的所有钥匙。

表4-6和表4-7展示了一些助记码的例子和它所生成的种子。

表4-6 128位熵的助记码以及所产生的种子

负熵输入 (128 bits)0c1e24e5917779d297e14d45f14e1a1a助记码 (12 个单词)army van defense carry jealous true garbage claim echo media make crunch种子 (512 bits)          3338a6d2ee71c7f28eb5b882159634cd46a898463e9d2d0980f8e80dfbba5b0fa0291e5fb88 8a599b44b93187be6ee3ab5fd3ead7dd646341b2cdb8d08d13bf

表4-7 256位熵的助记码以及所产生的种子

负熵输入 (256 bits)2041546864449caff939d32d574753fe684d3c947c3346713dd8423e74abcf8c助记码 (24个单词)cake apple borrow silk endorse fitness top denial coil riot stay wolf luggage oxygen faint major edit measure invite love trap fielddilemma oblige种子 (512 bits)          3972e432e99040f75ebe13a660110c3e29d131a2c808c7ee5f1631d0a977fcf473bee22fce540af281bf7cdeade0dd2c1c795bd02f1e4049e205a0158906c343

4.4.4 分层确定性钱包(BIP0032/BIP0044)

确定性钱包被开发成更容易从单个“种子”中生成许多关键的钥匙。最高级的来自确定性钱包的形是通过BIP0032标准生成的 the hierarchical deterministic wallet or HD wallet defined。分层确定性钱包包含从数结构所生成的钥匙。这种母钥匙可以生成子钥匙的序列。这些子钥匙又可以衍生出孙钥匙,以此无穷类推。这个树结构表如下图所示。

如果你想安装运行一个比特币钱包,你需要建造一个符合BIP0032和BIP0044标准的HD钱包。

HD钱包提供了随机(不确定性)钥匙有两个主要的优势。第一,树状结构可以被用来表达额外的组织含义。比如当一个特定分支的子密钥被用来接收交易收入并且有另一个分支的子密钥用来负责支付花费。不同分支的密钥都可以被用在企业环境中,这就可以支配不同的分支部门,子公司,具体功能以及会计类别。

HD钱包的第二个好处就是它可以允许让使用者去建立一个公共密钥的序列而不需要访问相对应的私钥。这可允许HD钱包在不安全的服务器中使用或者在每笔交易中发行不同的公共钥匙。公共钥匙不需要被预先加载或者提前衍生,但是在服务器中不具有可用来支付的私钥。

从种子中创造HD钱包

HD钱包从单个root seed中创建,为128到256位的随机数。HD钱包的所有的确定性都衍生自这个根种子。任何兼容HD钱包的根种子也可重新创造整个HD钱包。所以简单的转移HD钱包的根中斯就让HD钱包中所包含的成千上百万的密钥被复制,储存导出以及导入。根种子一般总是被表示为a mnemonic word sequence,正如\”4.4.3 助记码词汇\”一节所表述的,助记码词汇可以让人们更容易地抄写和储存。

创建主密钥以及HD钱包地主链代码的过程如下图所示。

根种子输入到HMAC-SHA512算法中就可以得到一个可用来创造master private key(m) 和 a master chain code的哈希。主私钥(m)之后可以通过使用我们在本章先前看到的那个普通椭圆曲线m * G过程生来成相对应的主公钥(M)。链代码可以给从母密钥中创造子密钥的那个方程中引入的熵。

私有子密钥的衍生

分层确定性钱包使用CKD(child key derivation)方程去从母密钥衍生出子密钥。

子密钥衍生方程是基于单项哈希方程。这个方程结合了:

• 一个母私钥或者公共钥匙(ECDSA未压缩键)• 一个叫做链码(256 bits)的种子• 一个索引号(32 bits)

链码是用来给这个过程引入看似的随机数据的,使得索引不能充分衍生其他的子密钥。因此,有了子密钥并不能让它发现自己的相似子密钥,除非你已经有了链码。最初的链码种子(在密码树的根部)是用随机数据构成的,随后链码从各自的母链码中衍生出来。

这三个项目相结合并散列可以生成子密钥,如下。

母公共钥匙——链码——以及索引号合并在一起并且用HMAC-SHA512方程散列之后可以产生512位的散列。所得的散列可被拆分为两部分。散列右半部分的256位产出可以给子链当链码。左半部分256位散列以及索引码被加载在母私钥上来衍生子私钥。在图4-11中,我们看到这种这个说明——索引集被设为0去生产母密钥的第0个子密钥(第一个通过索引)。

图4-11 延长母私钥去创造子私钥

改变索引可以让我们延长母密钥以及创造序列中的其他子密钥。比如子0,子1,子2等等。每一个母密钥可以右20亿个子密钥。

向密码树下一层重复这个过程,每个子密钥可以依次成为母密钥继续创造它自己的子密钥,直到无限代。

使用衍生的子密钥

子私钥不能从非确定性(随机)密钥中被区分出来。因为衍生方程是单向方程,所以子密钥不能被用来发现他们的母密钥。子密钥也不能用来发现他们的相同层级的姊妹密钥。如果你有第n个子密钥,你不能发现它前面的(第n-1)或者后面的子密钥(n+1)或者在同一顺序中的其他子密钥。只有母密钥以及链码才能得到所有的子密钥。没有子链码的话,子密钥也不能用来衍生出任何孙密钥。你需要同时有子密钥以及对应的链码才能创建一个新的分支来衍生出孙密钥。

那子私钥自己可被用做什么呢?它可以用来做公共钥匙和比特币地址。之后它就可以被用那个地址来签署交易和支付任何东西。

子密钥、对应的公共钥匙以及比特币地址都不能从随机创造的密钥和地址中被区分出来。事实是它们所在的序列,在创造他们的HD钱包方程之外是不可见的。一旦被创造出来,它们就和“正常”钥匙一样运行了。

扩展密钥

正如我们之前看到的,密钥衍生方程可以被用来创造钥匙树上任何层级的子密钥。这只需要三个输入量:一个密钥,一个链码以及想要的子密钥的索引。密钥以及链码这两个重要的部分被结合之后,就叫做extended key。术语“extended key”也被认为是“可扩展的密钥”是因为这种密钥可以用来衍生子密钥。

扩展密钥可以简单地被储存并且表示为简单的将256位密钥与256位链码所并联的512位序列。有两种扩展密钥。扩展的私钥是私钥以及链码的结合。它可被用来衍生子私钥(子私钥可以衍生子公共密钥)公共钥匙以及链码组成扩展公共钥匙。这个钥匙可以用来扩展子公共钥匙,见“4.1.6 生成公钥”。

想象一个扩展密钥作为HD钱包中钥匙树结构的一个分支的根。你可以衍生出这个分支的剩下所有部分。扩展私人钥匙可以创建一个完整的分支而扩展公共钥匙只能够创造一个公共钥匙的分支。

一个扩展钥匙包括一个私钥(或者公共钥匙)以及一个链码。一个扩展密钥可以创造出子密钥并且能创造出在钥匙树结构中的整个分支。分享扩展钥匙就可以访问整个分支。

扩展密钥通过Base58Check来编码,从而能轻易地在不同的BIP0032-兼容钱包之间导入导出。扩展密钥编码用的Base58Check使用特殊的版本号,这导致在Base58编码字符中,出现前缀“xprv”和“xpub”。这种前缀可以让编码更易被识别。因为扩展密钥是512或者513位,所以它比我们之前所看到的Base58Check-encoded串更长一些。

这是一个在Base58Check中编码的扩展私钥的例子:

xprv9tyUQV64JT5qs3RSTJkXCWKMyUgoQp7F3hA1xzG6ZGu6u6Q9VMNjGr67Lctvy5P8oyaYAL9CAWrUE9i6GoNMKUga5biW6Hx4tws2six3b9c

这是在Base58Check中编码的对应的扩展公共钥匙:

xpub67xpozcx8pe95XVuZLHXZeG6XWXHpGq6Qv5cmNfi7cS5mtjJ2tgypeQbBs2UAR6KECeeMVKZBPLrtJunSDMstweyLXhRgPxdp14sk9tJPW9公共子钥匙推导

正如之前提到的,分层确定性钱包的一个很有用的特点就是可以不通过私钥而直接从公共母钥匙派生出公共子钥匙的能力。这就给了我们两种去衍生子公共钥匙的方法:或者通过子私钥,再或者就是直接通过母公共钥匙。

因此,扩展的公共钥匙可以再HD钱包结构的分支中,被用来衍生所有的公钥(且只有公共钥匙)。

这种快捷方式可以用来创造非常保密的public-key-only配置。在配置中,服务器或者应用程序不管有没有私钥,都可以有扩展公共钥匙的副本。这种配置可以创造出无限数量的公共钥匙以及比特币地址。但是不可以花送到这个地址里的任何比特币。与此同时,在另一种更保险的服务器上,扩展私钥可以衍生出所有的对应的可签署交易以及花钱的私钥。

这种方案的一个常见的方案是安装一个扩展的公共钥匙在服务电商公共程序的网络服务器上。网络服务器可以使用这个公共钥匙衍生方程去给每一笔交易(比如客户的购物车)创造一个新的比特币地址。但为了避免被偷,网络服务商不会有任何私钥。没有HD钱包的话,唯一的方法就是在不同的安全服务器上创造成千上万个比特币地址,之后就提前上传到电商服务器上。这种方法比较繁琐而且要求持续的维护来确保电商服务器不“用光”公共钥匙。

这种解决方案的另一种常见的应用是冷藏或者硬件钱包。在这种情况下,扩展的私钥可以被储存在纸质钱包中或者硬件设备中(比如 Trezor 硬件钱包)与此同时扩展公共钥匙可以在线保存。使用者可以根据意愿创造“接收”地址而私钥可以安全地在线下被保存。为了支付资金,使用者可以使用扩展的私钥离线签署比特币客户或者通过硬件钱包设备(比如Trezor)签署交易。图4-12阐述了扩展母公共钥匙来衍生子公共钥匙的传递机制。

图4-12 扩展母公共钥匙来创造一个子公共钥匙

硬化子密钥的衍生

从扩展公共钥匙衍生一个分支公共钥匙的能力是很重要的,但牵扯一些风险。访问扩展公共钥匙并不能得到访问子私人密钥的途径。但是,因为扩展公共钥匙包含有链码,如果子私钥被知道或者被泄漏的话,链码就可以被用来衍生所有的其他子私钥。一个简单地泄露的私钥以及一个母链码,可以暴露所有的子密钥。更糟糕的是,子私钥与母链码可以用来推断母私钥。

为了应对这种风险,HD钱包使用一种叫做hardened derivation的替代衍生方程。这就“打破”了母公共钥匙以及子链码之间的关系。这个硬化衍生方程使用了母私钥去推到子链码,而不是母公共钥匙。这就在母/子顺序中创造了一道“防火墙”——有链码但并不能够用来推算子链码或者姊妹私钥。强化的衍生方程看起来几乎与一般的衍生的子私钥相同,不同的是是母私钥被用来输入散列方程中而不是母公共钥匙,如图4-13所示。

图4-13 子密钥的强化衍生;忽略了母公共密钥

当强化私钥衍生方程被使用时,得到的子私钥以及链码与使用一般衍生方程所得到的结果完全不同的。得到的密钥“分支”可以被用来生产不易被攻击的扩展公共钥匙,因为它所含的链码不能被用来开发或者暴露任何私钥。强化的衍生也因此被用来在上一层级,使用扩展公共钥匙的的密钥树中创造“间隙”。

简单地来说,如果你想要利用扩展公共钥匙的便捷来衍生公共钥匙的分支而不将你自己暴露在泄露扩展链码的风险下,你应该从强化母私钥,而不是一般的母私钥,来衍生公共钥匙。最好的方式是,为了避免了推到出主钥匙,主钥匙所衍生的第一层级的子钥匙最好使用强化衍生。

正常衍生和强化衍生的索引号码

用在衍生方程中的索引号码是32位的整数。为了区分密钥是从正常衍生方程中衍生出来还是从强化衍生方程中产出,这个索引号被分为两个范围。索引号在0和231–1(0x0 to 0x7FFFFFFF)之间的是只被用在常规衍生。索引号在231和232–1(0x80000000 to 0xFFFFFFFF)之间的只被用在强化衍生方程。因此,索引号小于231就意味着子密钥是常规的,而大于或者等于231的子密钥就是强化型的。

为了让索引号码更容易被阅读和展示,强化子密码的索引号码是从0开始展示的,但是右上角有一个小撇号。第一个常规子密钥因此被表述为0,但是第一个强化子密钥(索引号为0x80000000)就被表示为0\’。第二个强化密钥依序有了索引号0x80000001,且被显示为1\’,以此类推。当你看到HD钱包索引号i\’,这就意味着 231+i。

HD钱包密钥识别符(路径)

HD钱包中的密钥是用“路径”命名的,且每个级别之间用斜杠(/)字符来表示(见表4-8)。由主私钥衍生出的私钥起始以“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个子密钥。

表4-8 HD钱包路径的例子

HD path密钥描述m/0从主私钥(m)衍生出的第一个(0)子密钥。m/0/0第一个私人子密钥(m/0)的子密钥。m/0\’/0第一个子强化密钥first hardened child(m/0\’)的第一个常规子密钥。m/1/0第2个子密钥(m/1)的第一个常规子密钥M/23/17/0/0主密钥衍生出的第24个子密钥所衍生出的第17个子密钥的第一个子密钥所衍生出的第一个子密钥。

HD钱包树状结构的导航

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

第一层的目的地总是被设定为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。表4-9展示了更多的例子。

表4-9 BIP0044 HD 钱包结构的例子

HD 路径主要描述M/44\’/0\’/0\’/0/2第三个收到公共钥匙的主比特币账户M/44\’/0\’/3\’/1/14第十五改变地址公钥的第四个比特币账户m/44\’/2\’/0\’/0/1为了签署交易的在莱特币主账户的第二个私钥

使用比特币浏览器实验比特币钱包

依据第3章介绍的使用比特币浏览管理器命令工具,你可以试着生产和延伸BIP0032确定性密钥以及将它们用不同的格式进行展示:

$ sx hd-seed > m # create a new master private key from a seed and store in file \”m\”$ cat m # show the master extended private key96 | Chapter 4: Keys, Addresses, Walletsxprv9s21ZrQH143K38iQ9Y5p6qoB8C75TE71NfpyQPdfGvzghDt39DHPFpovvtWZaR- gY5uPwV7RpEgHs7cvdgfiSjLjjbuGKGcjRyU7RGGSS8Xa$ cat m | sx hd-pub 0 # generate the M/0 extended public key xpub67xpozcx8pe95XVuZLHXZeG6XWXHpGq6Qv5cmNfi7cS5mtjJ2tgypeQbBs2UAR6KE- CeeMVKZBPLrtJunSDMstweyLXhRgPxdp14sk9tJPW9$ cat m | sx hd-priv 0 # generate the m/0 extended private key xprv9tyUQV64JT5qs3RSTJkXCWKMyUgoQp7F3hA1xzG6ZGu6u6Q9VMNjGr67Lctvy5P8oyaYAL9CA- WrUE9i6GoNMKUga5biW6Hx4tws2six3b9c$ cat m | sx hd-priv 0 | sx hd-to-wif # show the private key of m/0 as a WIF L1pbvV86crAGoDzqmgY85xURkz3c435Z9nirMt52UbnGjYMzKBUN$ cat m | sx hd-pub 0 | sx hd-to-address # show the bitcoin address of M/0 1CHCnCjgMNb6digimckNQ6TBVcTWBAmPHK$ cat m | sx hd-priv 0 | sx hd-priv 12 –hard | sx hd-priv 4 # generate m/ 0/12\’/4 xprv9yL8ndfdPVeDWJenF18oiHguRUj8jHmVrqqD97YQHeTcR3LCeh53q5PXPkLsy2kRaqgwoS6YZ- BLatRZRyUeAkRPe1kLR1P6Mn7jUrXFquUt

广告位
本文来自网络,不代表区块链网站|NFTS立场,转载请注明出处:https://www.qklwz.com/btb/qianbao/14999.html

作者: 花木

上一篇
下一篇

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

返回顶部