比特币地址详解

96
古萧龙城好青年
2019.01.21 18:44 字数 14979

SOURCE: https://blog.msiter.com/BIP32-39-43-44-20180323.html

H2

私钥 公钥 比特币地址

一个比特币钱包中包含一系列的密钥对,每个密钥对包括一个私钥和一个公钥。私钥(k)是一个数字,通常是随机选出的。

有了私钥,我们就可以使用椭圆曲线乘法这个单向加密函数产生一个公钥(K)。

有了公钥(K),我们就可以使用一个单向加密哈希函数生成比特币地址(A)。

公私钥单向关系示意图

H3 

私钥

私钥就是一个随机选出的数字而已。一个比特币地址中的所有资金的控制取决于相应私钥的所有权和控制权。在比特币交易中,私钥用于生成支付比特币所必需的签名以证明资金的所有权。私钥必须始终保持机密,因为一旦被泄露给第三方,相当于该私钥保护之下的比特币也拱手相让了。私钥还必须进行备份,以防意外丢失,因为私钥一旦丢失就难以复原,其所保护的比特币也将永远丢失。

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

H3 

公钥

通过椭圆曲线算法可以从私钥计算得到公钥,这是不可逆转的过程:K = k * G。其中k是私钥,G是被称为生成点的常数点,而K是所得公钥。其反向运算,被称为“寻找离散对数”——已知公钥K来求出私钥k——是非常困难的,就像去试验所有可能的k值,即暴力搜索。

H3 

比特币地址

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

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

在交易中,比特币地址通常以收款方出现。如果把比特币交易比作一张支票,比特币地址就是收款人,也就是我们要写入收款人一栏的内容。一张支票的收款人可能是某个银行账户,也可能是某个公司、机构,甚至是现金支票。支票不需要指定一个特定的账户,而是用一个普通的名字作为收款人,这使它成为一种相当灵活的支付工具。与此类似,比特币地址的使用也使比特币交易变得很灵活。比特币地址可以代表一对公钥和私钥的所有者,也可以代表其它东西,比如“P2SH

(Pay-to-Script-Hash)”付款脚本。

H4 

如何生成比特币地址

比特币地址可由公钥经过单向的加密哈希算法得到。哈希算法是一种单向函数,接收任意长度的输入产生指纹摘要。加密哈希函数在比特币中被广泛使用:比特币地址、脚本地址以及在挖矿中的工作量证明算法。由公钥生成比特币地址时使用的算法是Secure

Hash Algorithm (SHA)和the RACE Integrity Primitives Evaluation Message

Digest (RIPEMD),特别是SHA256和RIPEMD160。

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

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

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

H2 

私钥 公钥 格式

H3 

公钥的格式

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

我们从前文可知,公钥是在椭圆曲线上的一个点,由一对坐标(x,y)组成。公钥通常表示为前缀04紧接着两个256比特的数字。其中一个256比特数字是公钥的x坐标,另一个256比特数字是y坐标。前缀04是用来区分非压缩格式公钥,压缩格式公钥是以02或者03开头。

下面是由前文中的私钥所生成的公钥,其坐标x和y如下:

x=F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341Ay=07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

下面是同样的公钥以520比特的数字(130个十六进制数字)来表达。这个520比特的数字以前缀04开头,紧接着是x及y坐标,组成格式为04 x y

K=04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

H4 

压缩格式化公钥

引入压缩格式公钥是为了减少比特币交易的字节数,从而可以节省那些运行区块链数据库的节点磁盘空间。大部分比特币交易包含了公钥,用于验证用户的凭据和支付比特币。每个公钥有520比特(包括前缀,x坐标,y坐标)。如果每个区块有数百个交易,每天有成千上万的交易发生,区块链里就会被写入大量的数据。

一个公钥是一个椭圆曲线上的点(x,

y)。而椭圆曲线实际是一个数学方程,曲线上的点实际是该方程的一个解。因此,如果我们知道了公钥的x坐标,就可以通过解方程y2 mod p =

(x3 + 7) mod

p得到y坐标。这种方案可以让我们只存储公钥的x坐标,略去y坐标,从而将公钥的大小和存储空间减少了256比特。每个交易所需要的字节数减少了近一半,随着时间推移,就大大节省了很多数据传输和存储。

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

公钥压缩

下面是前述章节所生成的公钥,使用了264比特(66个十六进制数字)的压缩格式公钥格式,其中前缀03表示y坐标是一个奇数

K=03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

带来的问题:

这个压缩格式公钥对应着同样的一个私钥,这意味它是由同样的私钥所生成。但是压缩格式公钥和非压缩格式公钥差别很大。更重要的是,如果我们使用双哈希函数(RIPEMD160(SHA256(K)))将压缩格式公钥转化成比特币地址,得到的地址将会不同于由非压缩格式公钥产生的地址。这种结果会让人迷惑,因为一个私钥可以生成两种不同格式的公钥——压缩格式和非压缩格式,而这两种格式的公钥可以生成两个不同的比特币地址。但是,这两个不同的比特币地址的私钥是一样的。

压缩格式公钥渐渐成为了各种不同的比特币客户端的默认格式,它可以大大减少交易所需的字节数,同时也让存储区块链所需的磁盘空间变小。然而,并非所有的客户端都支持压缩格式公钥,于是那些较新的支持压缩格式公钥的客户端就不得不考虑如何处理那些来自较老的不支持压缩格式公钥的客户端的交易。这在钱包应用导入另一个钱包应用的私钥的时候就会变得尤其重要,因为新钱包需要扫描区块链并找到所有与这些被导入私钥相关的交易。比特币钱包应该扫描哪个比特币地址呢?新客户端不知道应该使用哪个公钥:因为不论是通过压缩的公钥产生的比特币地址,还是通过非压缩的公钥产生的地址,两个都是合法的比特币地址,都可以被私钥正确签名,但是他们是完全不同的比特币地址。

为了解决这个问题,当私钥从钱包中被导出时,较新的比特币客户端将使用一种不同的钱包导入格式(Wallet Import

Format)。这种新的钱包导入格式可以用来表明该私钥已经被用来生成压缩的公钥,同时生成的比特币地址也是基于该压缩的公钥。这个方案可以解决导入私钥来自于老钱包还是新钱包的问题,同时也解决了通过公钥生成的比特币地址是来自于压缩格式公钥还是非压缩格式公钥的问题。最后新钱包在扫描区块链时,就可以使用对应的比特币地址去查找该比特币地址在区块链里所发生的交易。

H3 

私钥的格式化

实际上“压缩格式私钥”是一种名称上的误导,因为当一个私钥被使用WIF压缩格式导出时,不但没有压缩,而且比“非压缩格式”私钥长出一个字节。这个多出来的一个字节是私钥被加了后缀01,用以表明该私钥是来自于一个较新的钱包,只能被用来生成压缩的公钥。私钥是非压缩的,也不能被压缩。“压缩的私钥”实际上只是表示“用于生成压缩格式公钥的私钥”,而“非压缩格式私钥”用来表明“用于生成非压缩格式公钥的私钥”。为避免更多误解,应该只可以说导出格式是“WIF压缩格式”或者“WIF”,而不能说这个私钥是“压缩”的。

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

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

H3 

比特币地址格式

通常用户见到的比特币地址是经过“Base58Check”编码的,这种编码使用了58个字符(一种Base58数字系统)和校验码,提高了可读性、避免歧义并有效防止了在地址转录和输入中产生的错误。Base58Check编码也被用于比特币的其它地方,例如比特币地址、私钥、加密的密钥和脚本哈希中,用来提高可读性和录入的正确性。

比特币地址生成流程

H4 

Base58和Base58Check编码

为了更简洁方便地表示长串的数字,许多计算机系统会使用一种以数字和字母组成的大于十进制的表示法。例如,传统的十进制计数系统使用0-9十个数字,而十六进制系统使用了额外的

A-F

六个字母。一个同样的数字,它的十六进制表示就会比十进制表示更短。更进一步,Base64使用了26个小写字母、26个大写字母、10个数字以及两个符号(例如“+”和“/”),用于在电子邮件这样的基于文本的媒介中传输二进制数据。Base64通常用于编码邮件中的附件。Base58是一种基于文本的二进制编码格式,用在比特币和其它的加密货币中。这种编码格式不仅实现了数据压缩,保持了易读性,还具有错误诊断功能。Base58是Base64编码格式的子集,同样使用大小写字母和10个数字,但舍弃了一些容易错读和在特定字体中容易混淆的字符。具体地,Base58不含Base64中的0(数字0)、O(大写字母o)、l(小写字母L)、I(大写字母i),以及“+”和“/”两个字符。简而言之,Base58就是由不包括(0,O,l,I)的大小写字母和数字组成。

比特币的Base58字母表

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

Base58Check是一种常用在比特币中的Base58编码格式,增加了错误校验码来检查数据在转录中出现的错误。校验码长4个字节,添加到需要编码的数据之后。校验码是从需要编码的数据的哈希值中得到的,所以可以用来检测并避免转录和输入中产生的错误。使用Base58check编码格式时,编码软件会计算原始数据的校验码并和结果数据中自带的校验码进行对比。二者不匹配则表明有错误产生,那么这个Base58Check格式的数据就是无效的。例如,一个错误比特币地址就不会被钱包认为是有效的地址,否则这种错误会造成资金的丢失。

H4 

Base58编码生成流程

为了使用Base58Check编码格式对数据(数字)进行编码,首先我们要对数据添加一个称作“版本字节”的前缀,这个前缀用来明确需要编码的数据的类型。例如,比特币地址的前缀是0(十六进制是0x00),而对私钥编码时前缀是128(十六进制是0x80)。

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

checksum=SHA256(SHA256(prefix+data))

在产生的长32个字节的哈希值(两次哈希运算)中,我们只取前4个字节。这4个字节就作为校验码。校验码会添加到数据之后。

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


Base58 编码流程

我就一展示数据的表格

种类版本前缀 (hex)Base58格式

Bitcoin Address0x001

Pay-to-Script-Hash Address0x053

Bitcoin Testnet Address0x6Fm or n

Private Key WIF0x805, K or L

BIP38 Encrypted Private Key0x01426P

BIP32 Extended Public Key0x0488B21Expub

H2 

比特币钱包

钱包是私钥的容器,通常通过有序文件或者简单的数据库实现。另外一种制作私钥的途径是

确定性密钥生成。在这里你可以用原先的私钥,通过单向哈希函数来生成每一个新的私钥,并将新生成的密钥按顺序连接。只要你可以重新创建这个序列,你只需要第一个私钥(称作种子、主私钥)来生成它们。

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

H3 

非确定性(随机)钱包

在最早的一批比特币客户端中,钱包只是随机生成的私钥集合。这种类型的钱包被称作零型非确定钱包。举个例子,比特币核心客户端预先生成100个随机私钥,从最开始就生成足够多的私钥并且每把钥匙只使用一次。这种类型的钱包有一个昵称“Just

a Bunch Of

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


非确定性(随机)钱包

H3 

确定性(种子)钱包

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

H3 

助记码词汇

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

建议使用若干个助记词来替换之前直接生成随机数的方式,通过助记词推导出随机的Master Seed。

H4 

助记词列表

其中WordList,是存在这里的。

包含以下语言,每种语言都存在2048个单词,来成为助记词备选者。

每一种语言都提供对了对应的2048个词。其中都包含了,英文,日文,汉语,西班牙语,简体中文,繁体中文,法语,意大利。

EnglishJapaneseKoreanSpanishChinese(Simplified)Chinese(Traditional)FrenchItalian

因为这些词语是通过随机的 128-256bits随机种子产生的助记词。所以,这些词是随机的,并且不可修改

H5 

这些词是不是安全的?

好吧,我觉得 随机的东西… 主要有足够的时间,无限猴子理论。我只要重复实验下去,绝对会有这么一天,到时候,我会和另一个人拥有同样的Master Seed!!!

当然….几率有多小呢。。。通过 以下公式,我们可以得出在m个数据中,抽取n个数据的组合方式个数。

Amn=n(n−1)…(n−m+1)=n!(n−m)!

假设使用的是 128为的随机种子,产生了12个助记词。也就是在2048个数据中提取12个,总数为:

2048!(2048−12)!

这个还只是,没有排列顺序的方式,他抽取的助记词,顺序也是一种随机因素。所以只会比这个还大。

好吧,还是不好理解,那么我们来说一下我们熟知的双色球红球概率。

33!(33−6)!

有797 448 960个组合方式,这个几率就已经很少有人中奖了… 何况这么大…

emmmmm… 至少,我算不出来他的几率是多少。。。因为光是2048 的阶乘,,,就写满了一页纸。。。。

H4 

生成的方式

接下来会说明下,生成助记词和通过助记词生成 Master Seed 的原理

H4 

生成助记词的过程

规定熵的位数必须是 32 的整数倍,所以熵的长度取值位 128 到 256 之间取 32 的整数倍的值,分别为 128, 160, 192, 224, 256;

校验和的长度为熵的长度/32 位, 所以校验和长度可为 4,5,6,7,8 位

助记词库有 2048 个词,用 11 位可全部定位词库中所有的词,作为词的索引,故一个词用 11 位表示,助记词的个数可为 (熵+校验和)/11,值为 12,15,18,21,24

我就一展示数据的表格

熵(bits)校验和(bits)熵+校验和(bits)助记词

128(128/32)4132(132/11) -> 12

160(160/32)4165(132/11) -> 15

192(192/32)4198(132/11) -> 18

224(224/32)4231(132/11) -> 21

256(256/32)4264(132/11) -> 24

## CS 为 校验和的长度CS=ENT/32## MS 为 助记词的个数MS=(ENT+CS)/11|ENT|CS|ENT+CS|MS|+-------+----+--------+------+|128|4|132|12||160|5|165|15||192|6|198|18||224|7|231|21||256|8|264|24|

步骤如下:

生成一个长度为 128~256 位 (bits) 的随机序列(熵)

取熵哈希后的前 n 位作为校验和 (n= 熵长度/32)

随机序列 + 校验和 -> 拼接起来

把步骤三得到的结果每 11 位切割

步骤四得到的每 11 位字节(二进制转为->十进制)通过Index获取匹配词库的一个词

步骤五得到的结果就是助记词串


生成助记词过程

H4 

助记词生成 Master Seed的过程

为了从助记词中生成二进制种子,BIP39 采用PBKDF2函数推算种子,其参数如下:

助记词句子作为密码

“mnemonic” + passphrase 作为盐

2048 作为重复计算的次数

HMAC-SHA512 作为随机算法

512 位(64 字节)是期望得到的密钥长度


助记词推出随机种子

H3 

分层确定性钱包

确定性钱包被开发成更容易从单个“种子”中生成许多关键的钥匙。最高级的来自确定性钱包的形是通过BIP0032标准生成的

the hierarchical deterministic wallet or HD wallet

defined。分层确定性钱包包含从数结构所生成的钥匙。这种母钥匙可以生成子钥匙的序列。这些子钥匙又可以衍生出孙钥匙,以此无穷类推。这个树结构表如下图所示。


分层确定性钱包

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

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

H4 

从种子中创造HD钱包

HD钱包从单个root

seed中创建,为128到256位的随机数。HD钱包的所有的确定性都衍生自这个根种子。任何兼容HD钱包的根种子也可重新创造整个HD钱包。所以简单的转移HD钱包的根种子就让HD钱包中所包含的成千上百万的密钥被复制,储存导出以及导入。根种子一般总是被表示为a

mnemonic word sequence,助记码词汇可以让人们更容易地抄写和储存。

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


创建主密钥以及HD钱包地主链代码的过程

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

H4 

私有子秘钥的衍生

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

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

一个母私钥或者公共钥匙(ECDSA未压缩键)

一个叫做链码(256 bits)的种子

一个索引号(32 bits)

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

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

母公共钥匙——链码——以及索引号合并在一起并且用HMAC-SHA512方程散列之后可以产生512位的散列。所得的散列可被拆分为两部分。散列右半部分的256位产出可以给子链当链码。左半部分256位散列以及索引码被加载在母私钥上来衍生子私钥。


延长母私钥去创造子私钥

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

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

H4 

使用衍生的子密钥

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

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

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

H4 

扩展秘钥

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

key。术语“extended key”也被认为是“可扩展的密钥”是因为这种密钥可以用来衍生子密钥。

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

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

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

H4 

公共子钥匙推导

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

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

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


扩展母公共钥匙来创造一个子公共钥匙

H4 

硬化子密钥的衍生

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

为了应对这种风险,HD钱包使用一种叫做hardened

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


子密钥的强化衍生;忽略了母公共密钥

H4 

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

用在衍生方程中的索引号码是32位的整数。为了区分密钥是从正常衍生方程中衍生出来还是从强化衍生方程中产出,这个索引号被分为两个范围。索引号在0和231

231–1(0x0 to 0x7FFFFFFF)之间的是只被用在常规衍生。索引号在231和232–1

(0x80000000 to 0xFFFFFFFF)之间的只被用在强化衍生方程。因此,索引号小于231就意味着子密钥是常规的,而大于或者等于231的子密钥就是强化型的。

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

H4 

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个子密钥

我就一展示数据的表格

HD path密钥描述

m/0从主私钥(m)衍生出的第一个(0)子密钥。

m/0/0第一个私人子密钥(m/0)的子密钥。

m/0’/0第一个子强化密钥first hardened child(m/0’)的第一个常规子密钥。

H4 

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。

我就一展示数据的表格

HD 路径主要描述

M/44’/0’/0’/0/2第三个收到公共钥匙的主比特币账户

M/44’/0’/3’/1/14第十五改变地址公钥的第四个比特币账户

m/44’/2’/0’/0/1为了签署交易的在莱特币主账户的第二个私钥

H2 

扩展知识 高级密钥和地址

H3 

加密私钥(BIP0038)

私钥必须保密。私钥的机密性需求事实情况是,在实践中相当难以实现,因为该需求与同样重要的安全对象可用性相互矛盾。当你需要为了避免私钥丢失而存储备份时,会发现维护私钥私密性是一件相当困难的事情。通过密码加密内有私钥的钱包可能要安全一点,但那个钱包也需要备份。有时,例如用户因为要升级或重装钱包软件,而需要把密钥从一个钱包转移到另一个。私钥备份也可能需要存储在纸张上或者外部存储介质里,比如U盘。但如果一旦备份文件失窃或丢失呢?这些矛盾的安全目标推进了便携、方便、可以被众多不同钱包和比特币客户端理解的加密私钥标准BIP0038的出台。

BIP0038提出了一个通用标准,使用一个口令加密私钥并使用Base58Check对加密的私钥进行编码,这样加密的私钥就可以安全地保存在备份介质里,安全地在钱包间传输,保持密钥在任何可能被暴露情况下的安全性。这个加密标准使用了AES,这个标准由NIST建立,并广泛应用于商业和军事应用的数据加密。

BIP0038加密方案是:输入一个比特币私钥,通常使用WIF编码过,base58chek字符串的前缀“5”。此外BIP0038加密方案需要一个长密码作为口令,通常由多个单词或一段复杂的数字字母字符串组成。BIP0038加密方案的结果是一个由base58check编码过的加密私钥,前缀为6P。如果你看到一个6P开头的的密钥,这就意味着该密钥是被加密过,并需要一个口令来转换(解码)该密钥回到可被用在任何钱包WIF格式的私钥(前缀为5)。许多钱包APP现在能够识别BIP0038加密过的私钥,会要求用户提供口令解码并导入密钥。第三方APP,诸如非常好用基于浏览器的Bit

Address,可以被用来解码BIP00038的密钥。

最通常使用BIP0038加密的密钥用例是纸钱包——一张纸张上备份私钥。只要用户选择了强口令,使用BIP0038加密的私钥的纸钱包就无比的安全,这也是一种很棒的比特币离线存储方式(也被称作“冷存储”)。

我就一展示数据的表格

私钥(WIF)5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

密码MyTestPassphrase

加密私钥(BIP0038)6PRTHL6mWa48xSopbU1cKrVjpKbBZxcLRRCdctLJ3z5yxE87MobKoXdTsJ

H3 

P2SH (Pay-to-Script Hash)和多重签名地址

正如我们所知,传统的比特币地址从数字1开头,来源于公钥,而公钥来源于私钥。虽然任何人都可以将比特币发送到一个1开头的地址,但比特币只能在通过相应的私钥签名和公钥哈希值后才能消费。

以数字3开头的比特币地址是P2SH地址,有时被错误的称谓多重签名或多重签名地址。他们指定比特币交易中受益人作为哈希的脚本,而不是公钥的所有者。这个特性在2012年1月由BIP0016引进,目前因为BIP0016提供了增加功能到地址本身的机会而被广泛的采纳。不同于P2PKH交易发送资金到传统1开头的比特币地址,资金被发送到3开头的地址时,需要的不仅仅是一个公钥的哈希值,同时也需要一个私钥签名作为所有者证明。在创建地址的时候,这些要求会被定义在脚本中,所有对地址的输入都会被这些要求阻隔。

一个P2SH地址从事务脚本中创建,它定义谁能消耗这个事务输出。编码一个P2SH地址涉及使用一个在创建比特币地址用到过的双重哈希函数,并且只能应用在脚本而不是公钥:

script hash=RIPEMD160(SHA256(script))

脚本哈希的结果是由Base58Check编码前缀为5的版本、编码后得到开头为3的编码地址。一个P2SH地址例子是32M8ednmuyZ2zVbes4puqe44NZumgG92sM。

P2SH 不一定是多重签名的交易。虽然P2SH地址通常都是代表多重签名,但也可能是其他类型的交易脚本。

H4 

多重签名地址和P2SH

目前,P2SH函数最常见的实现是用于多重签名地址脚本。顾名思义,底层脚本需要多个签名来证明所有权,此后才能消费资金。设计比特币多重签名特性是需要从总共N个密钥中需要M个签名(也被称为“阈值”),被称为M-N多签名,其中M是等于或小于N。例如,第一章中提到的咖啡店主鲍勃使用多重签名地址需要1-2签名,一个是属于他的密钥和一个属于他同伴的密钥,以确保其中一方可以签署度过一个事务锁定输出到这个地址。这类似于传统的银行中的一个“联合账户”,其中任何一方配偶可以凭借单一签名消费。或Gopesh,

Bob雇佣的网页设计师创立一个网站,可能为他的业务需要一个2-3的多签名地址,确保没有资金会被花费除非至少两个业务合作伙伴签署这笔交易。

我们将会在第五章节探索如何使用P2SH地址创建事务用来消费资金。

H3 

比特币靓号地址

靓号地址包含了可读信息的有效比特币地址。例如,1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33就是包含了Base-58字母love的。靓号地址需要生成并通过数十亿的候选私钥测试,直到一个私钥能生成具有所需图案的比特币地址。虽然有一些优化过的靓号生成算法,该方法必须涉及随机上选择一个私钥,生成公钥,再生成比特币地址,并检查是否与所要的靓号图案相匹配,重复数十亿次,直到找到一个匹配。

一旦找到一个匹配所要图案的靓号地址,来自这个靓号地址的私钥可以和其他地址相同的方式被拥有者消费比特币。靓号地址不比其他地址具有更多安全性。它们依靠和其他地址相同的ECC和SHA。你无法比任何别的地址更容易的获得一个靓号图案开头的私钥。

例如:我们介绍了Eugenia,一位在菲律宾工作的儿童慈善总监。我们假设Eugenia组织了一场比特币募捐活动,并希望使用靓号比特币地址来宣布这个募捐活动。Eugenia将会创造一个以1Kids开头的靓号地址来促进儿童慈善募捐的活动。让我们看看这个靓号地址如何被创建,这个靓号地址对Eugenia慈善募捐的安全性又意味着什么。

H4 

生成靓号地址

我们必须认识到使用来自Base58字母表中简单符号来代表比特币地址是非常重要的。搜索“1kids”开头的图案我们会发现从1Kids11111111111111111111111111111到1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz的地址。这些以“1kid”开头的地址范围中大约有58的29次方地址

我们把“1Kids”这个前缀当作数字,我们可以看看比特币地址中这个前缀出现的频率。如果是一台普通性能的桌面电脑,没有任何特殊的硬件,可以每秒发现大约10万个密钥。

我就一展示数据的表格

长度地址前缀概率平均生成时间

11K158

< 1毫秒

21Ki13364

50毫秒

31Kid1195×103

< 2秒

41Kids111×106

1分钟

51KidsC1656×106

1小时

61KidsCh138×109

2天

71KidsCha12.2×1012

3–4 月

81KidsChar1128×1012

13–18年

91KidsChari17×1015

800年

101KidsCharit1400×1015

46,000年

111KidsCharity123×1018

250万年

正如你所见,Eugenia将不会很快地创建出以“1KidsCharity”开头的靓号地址,即使她有数千台的电脑同时进行运算。每增加一个字符就会增加58倍的计算难度。超过七个字符的搜索模式通常需要专用的硬件才能被找出,譬如用户定制的具有多图形处理单元(GPU)的桌面级设备。那些通常是无法继续在比特币挖矿中盈利的钻机,被重新赋予了寻找靓号地址的任务。用GPU系统搜索靓号的速度比用通用CPU要快很多个量级。

另一种寻找靓号地址的方法是将工作外包给一个矿池里的靓号矿工们,如靓号矿池中的矿池。一个矿池是一种允许那些GPU硬件通过为他人寻找靓号地址来获得比特币的服务。对小额的账单,Eugenia可以外包搜索模式为7个字符靓号地址寻找工作,在几个小时内就可以得到结果,而不必用一个CPU搜索上几个月才得到结果。

生成一个靓号地址是一项通过蛮力的过程:尝试一个随机密钥,检查结果地址是否和所需的图案相匹配,重复这个过程直到成功找到为止

H5 

靓号挖掘程序

#include<bitcoin/bitcoin.hpp>// The string we are searching forconststd::string search="1kid";// Generate a random secret key. A random 32 bytes.bc::ec_secretrandom_secret(std::default_random_engine&engine);// Extract the Bitcoin address from an EC secret.std::stringbitcoin_address(constbc::ec_secret&secret);// Case insensitive comparison with the search string.boolmatch_found(conststd::string&address);intmain(){std::random_device random;std::default_random_engineengine(random());// Loop continuously...while(true){// Generate a random secret.bc::ec_secret secret=random_secret(engine);// Get the address.std::string address=bitcoin_address(secret);// Does it match our search string? (1kid)if(match_found(address)){// Success!std::cout<<"Found vanity address! "<<address<<std::endl;std::cout<<"Secret: "<<bc::encode_hex(secret)<<std::endl;return0;}}// Should never reach here!return0;}bc::ec_secretrandom_secret(std::default_random_engine&engine){// Create new secret...bc::ec_secret secret;// Iterate through every byte setting a random value... for (uint8_t& byte: secret)byte=engine()%std::numeric_limits<uint8_t>::max();// Return result.returnsecret;}std::stringbitcoin_address(constbc::ec_secret&secret){// Convert secret to pubkey...bc::ec_point pubkey=bc::secret_to_public_key(secret);// Finally create address.bc::payment_address payaddr;bc::set_public_key(payaddr,pubkey);// Return encoded form.returnpayaddr.encoded();}boolmatch_found(conststd::string&address){autoaddr_it=address.begin();// Loop through the search string comparing it to the lower case// character of the supplied address.for(autoit=search.begin();it!=search.end();++it,++addr_it)if(*it!=std::tolower(*addr_it))returnfalse;// Reached end of search string, so address matches.returntrue;}

示例程序需要用C编译器链接libbitcoin库(此库需要提前装入该系统)进行编译。直接执行vanity-miner的可执行文件,它就会尝试碰撞以“1kid”开头的比特币地址。

$ # Compile the code with g++

$ g++ -o vanity-miner vanity-miner.cpp $(pkg-config --cflags --libs libbitcoin) $ # Run the example

$ ./vanity-miner

Found vanity address! 1KiDzkG4MxmovZryZRj8tK81oQRhbZ46YT

Secret: 57cc268a05f83a23ac9d930bc8565bac4e277055f4794cbd1a39e5e71c038f3f

$ # Run it again for a different result

$ ./vanity-miner

Found vanity address! 1Kidxr3wsmMzzouwXibKfwTYs5Pau8TUFn

Secret: 7f65bbbbe6d8caae74a0c6a0d2d7b5c6663d71b60337299a1a2cf34c04b2a623

# Use "time" to see how long it takes to find a result

$ time ./vanity-miner

Found vanity address! 1KidPWhKgGRQWD5PP5TAnGfDyfWp5yceXM

Secret: 2a802e7a53d8aa237cd059377b616d2bfcfa4b0140bc85fa008f2d3d4b225349

real    0m8.868s

user    0m8.828s

sys    0m0.035s

正如我们运行Unix命令time所测出的运行时间所示,示例代码要花几秒钟来找出匹配“kid”三个字符模板的结果。读者们可以在源代码中改变search这一搜索模板,看一看如果是四个字符或者五个字符的搜索模板需要花多久时间!

H4 

靓号地址安全性

靓号地址既可以增加、也可以削弱安全措施,它们着实是一把双刃剑。用于改善安全性时,一个独特的地址使对手难以使用他们自己的地址替代你的地址,以欺骗你的顾客支付他们的账单。不幸的是,靓号地址也可能使得任何人都能创建一个类似于随机地址的地址,甚至另一个靓号地址,从而欺骗你的客户。

Eugenia可以让捐款人捐款到她宣布的一个随机生成地址(例如:1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy)。或者她可以生成一个以“1Kids”开头的靓号地址以显得更独特。

在这两种情况下,使用单一固定地址(而不是每比捐款用一个独立的动态地址)的风险之一是小偷有可能会黑进你的网站,用他自己的网址取代你的网址,从而将捐赠转移给自己。如果你在不同的地方公布了你的捐款地址,你的用户可以在付款之前直观地检查以确保这个地址跟在你的网站、邮件和传单上看到的地址是同一个。在随机地址1j7mdg5rbqyuhenydx39wvwk7fslpeoxzy的情况下,普通用户可能会只检查头几个字符“1j7mdg”,就认为地址匹配。使用靓号地址生成器,那些想通过替换类似地址来盗窃的人可以快速生成与前几个字符相匹配的地址

那靓号地址会不会增加安全性?如果Eugenia生成1Kids33q44erFfpeXrmDSz7zEqG2FesZEN的靓号地址,用户可能看到靓号图案的字母和一些字符在上面,例如在地址部分中注明了1Kids33。这样就会迫使攻击者生成至少6个字母相匹配的的靓号地址(比之前多2个字符),就要花费比Eugenia多3364倍的靓号图案。本质上,Eugenia付出的努力(或者靓号池付出的)迫使攻击者不得不生成更长的靓号图案。如果Eugenia花钱请矿池生成8个字符的靓号地址,攻击者将会被逼迫到10字符的境地,那将是个人电脑,甚至昂贵自定义靓号挖掘机或靓号池也无法生成。对Eugenia来说可承担的起支出,对攻击者来说则变成了无法承担支出,特别是如果欺诈的回报不足以支付生成靓号地址所需的费用。

H3 

纸钱包

纸钱包是打印在纸张上的比特币私钥。有时纸钱包为了方便起见也包括对应的比特币地址,但这并不是必要的,因为地址可以从私钥中导出。纸钱包是一个非常有效的建立备份或者线下存储比特币(即冷钱包)的方式。作为备份机制,一个纸钱包可以提供安全性,以防在电脑硬盘损坏、失窃或意外删除的情况下造成密钥的的丢失。作为一个冷存储的机制,如果纸钱包密钥在线下生成并永久不在电脑系统中存储,他们在应对黑客攻击,键盘记录器,或其他在线电脑欺骗更有安全性。

纸钱包有许多不同的形状,大小,和外观设计,但非常基本的原则是一个密钥和一个地址打印在纸张上。

区块链时代-开发
Web note ad 1