比特币的密码学

     比特币的所有权是通过数字密钥、比特币地址数字签名来确定的。

     数字密钥实际上并不存储在网络中,而是由用 户生成之后,存储在一个叫做钱包的文件或简单的数据库中。存储在用户钱包中的数字密钥完全独立于比特币协 议,可由用户的钱包软件生成并管理,而无需参照区块链或访问网络。密钥实现了比特币的许多有趣特性,包括去 中心化信任和控制、所 有权认证和基于密码学证明的安全模型。

    大多数比特币交易都需要一个有效的签名才会被存储在区块链。只有有效的密钥才能产生有效的数字签名,因此拥 有密钥副本就拥有了对该帐户的比特币的控制权。用于支出资金的数字签名也称为见证(witness),密码术中使 用的术语。 比特币交易中的见证数据证明了所用资金的真正归谁所有。

     密钥是成对出现的,由一个私钥和一个公钥所组成。公钥就像银行的帐号而私钥就像控制账户的PIN码或支票的 签名。比特币的用户很少会直接看到数字密钥。一般情况下,它们被存储在钱包文件内,由比特币钱包软件进行管 理。 

    在比特币交易的支付环节,收件人的公钥是通过其数字指纹代表的,称为比特币地址,就像支票上的支付对象的名 字 (即“收款方”)。一般情况下,比特币地址由一个公钥生成并对应于这个公钥。然而,并非所有比特币地址都是 公钥; 他们也可以代表其他支付对象,譬如脚本。这样一来,比特币地址把收款方抽象起 来了,使得交易的目的地更灵活,就像支票一样:这个支付工具可支付到个人账户、公司账户,进行账单支付或现 金支付。比特币地址是用户经常看到的密钥的唯一代表,他们只需要把比特币地址告诉其他人即可。

     公钥加密和加密货币 

    公钥加密发明于20世纪70年代。它是计算机和信息安全的数学基础。自从公钥加密被发明之后,一些合适的数学函数被发现,譬如:素数幂和椭圆曲线乘法。这些数学函数都是不可逆 的, 就是说很容易向一个方向计算,但不可以向相反方向倒推。基于这些数学函数的密码学,使得生成数字密钥和 不可伪造 的数字签名成为可能。比特币正是使用椭圆曲线乘法作为其公钥加密的基础。

     在比特币系统中,我们用公钥加密创建一个密钥对,用于控制比特币的获取。密钥对包括一个私钥,和由其衍生出 的唯 一的公钥。公钥用于接收比特币,而私钥用于比特币支付时的交易签名。公钥和私钥之间的数学关系,使得私钥可用于生成特定消息的签名。此签名可以在不泄露私钥的同时对公钥进行验 证。

    支付比特币时,比特币的当前所有者需要在交易中提交其公钥和签名(每次交易的签名都不同,但均从同一个私钥 生 成)。比特币网络中的所有人都可以通过所提交的公钥和签名进行验证,并确认该交易是否有效,即确认支付者 在该时刻对所交易的比特币拥有所有权。

   私钥和公钥 

    一个比特币钱包中包含一系列的密钥对,每个密钥对包括一个私钥和一个公钥。私钥(k)是一个数字,通常是随 机选出的。有了私钥,我们就可以使用椭圆曲线乘法这个单向加密函数产生一个公钥(K)。有了公钥(K),我们 就可以使 用一个单向加密哈希函数生成比特币地址(A)我们将从生成私钥开始,讲述如何使用椭圆 曲线运算将私 钥生成公钥,并终由公钥生成比特币地址。私钥、公钥和比特币地址之间的关系如下图所示。

比特币的密码学_第1张图片

    为什么使用非对称加密(公钥/私钥)? 为什么在比特币中使用非对称密码术? 它不是用于“加密”(make secret)交易。 相反,非对称密码学的有用属性 是生成数字签名的能力。 可以将私钥应用于交易的数字指纹以产生数字签名。 该签名只能由知晓私钥的人生成。 但是,访问公钥和交易指纹的任何人都可以使用它们来验证签名。 这种非对称密码学的适用性使得任何人都可以验 证每笔交易的每个签名,同时确保只有私钥的所有者可以产生有效的签名。 

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

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

    生成密钥的第一步也是重要的一步是要找到足够安全的熵源即随机性来源。生成一个比特币私钥在本质上 与“在1 到2^256之间选一个数字”无异。只要选取的结果是不可预测或不可重复的,那么选取数字的具体方法并不 重要。比特币软件使用操作系统底层的随机数生成器来产生256位的熵(随机性)。通常情况下,操作系统随机数 生成器由人工的随机源进行初始化,这就是为什么也可能需要不停晃动鼠标几秒钟。

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

     警告 不要自己写代码或使用你的编程语言提供的简易随机数生成器来获得一个随机数。使用密码学安全的伪随机数 生成器(CSPRNG),并且需要有一个来自具有足够熵值的源的种子。使用随机数发生器的程序库时,需仔细研读 其文档,以确保它是加密安全的。正确实施CSPRNG是密钥安全性的关键所在。

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

    提示比特币私钥空间的大小是2^256,这是一个非常大的数字。用十进制表示的话,大约是10^77,而可见宇宙被 估计只含有10^80个原子。 要使用比特币核心客户端生成一个新的密钥,可使用 getnewaddress 命令。出于安全考虑,命令运行后只 显示生 成的公钥,而不显示私钥。如果要bitcoind显示私钥,可以使用 dumpprivkey 命令。 dumpprivkey 命令会把私钥 以 Base58校验和编码格式显示,这种私钥格式被称为钱包导入格式(WIF,Wallet Import Format),在“私钥的 格式”一节有详细讲解。下面给出了使用这两个命令生成和显示私钥的例子: 

$ bitcoin-cli getnewaddress

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy 

$ bitcoin-cli dumpprivkey 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ 

dumpprivkey 命令打开钱包提取由 getnewaddress 命令生成的私钥。除非密钥对都存储在钱包里,否则bitcoind 的并不能从公钥得知私钥。 dumpprivkey 命令才有效。 

提示 dumpprivkey命令无法从公钥得到对应的私钥,因为这是不可能的。这个命令只是显示钱包中已有也就是由 getnewaddress命令生成的私钥。
您还可以使用Bitcoin Explorer命令行工具(请参阅附录中的[appdx_bx])使用命令seed,ec-new和ec-to-wif生成 和显示私钥:

$ bx seed | bx ec-new | bx ec-to-wif 

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn 

 
    通过椭圆曲线乘法可以从私钥计算得到公钥,这是不可逆转的过程:K = k * G 。其中k是私钥,G是被称为生成点 的常数点,而K是所得公钥。其反向运算,被称为“寻找离散对数”——已知公钥K来求出私钥k——是非常困难的, 就像去试验所有可能的k值,即暴力搜索。在演示如何从私钥生成公钥之前,我们先稍微详细学习下椭圆曲线密码 学。

    提示 椭圆曲线乘法是密码学家称之为“陷阱门”功能的一种函数:在一个方向(乘法)很容易做,而不可能在相反的 方向(除法)做。 私钥的所有者可以容易地创建公钥,然后与世界共享,知道没有人可以从公钥中反转函数并计算 出私钥。 这个数学技巧成为证明比特币资金所有权的不可伪造和安全的数字签名的基础。

 椭圆曲线密码学(Elliptic Curve Cryptography)

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

下图是一个椭圆曲线的示例,类似于比特币所用的曲线。

比特币的密码学_第2张图片

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

比特币的密码学_第3张图片

   上述mod p(素数p取模)表明该曲线是在素数阶p的有限域内,也写作Fp,其中

           p = 2^256 – 2^32 – 2^9 – 2^8 – 2^7 – 2^6 – 2^4 – 1, 这是个非常大的素数。

     因为这条曲线被定义在一个素数阶的有限域内,而不是定义在实数范围,它的函数图像看起来像分散在两个维度上 的散落的点,因此很难可视化。不过,其中的数学原理与实数范围的椭圆曲线相似。作为一个例子,下图显示了在 一个小了很多的素数阶17的有限域内的椭圆曲线,其形式为网格上的一系列散点。而secp256k1的比特币椭圆曲线 可以被想象成一个极大的网格上一系列更为复杂的散点。

比特币的密码学_第4张图片

    下面举一个例子,这是 secp256k1 曲线上的点P,其坐标为(x,y)。可以使用Python对其检验:

P=(55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)

     在椭圆曲线的数学原理中,有一个点被称为“无穷远点”,这大致对应于0在加法中的作用。计算机中,它有时表示 为X = Y = 0(虽然这不满足椭圆曲线方程,但可作为特殊情况进行检验)。 还有一个 + 运算符,被称为“加法”,就 像小学数学中的实数相加。给定椭圆曲线上的两个点P1和P2,则椭圆曲线上必定有第三点 P3 = P1 + P2。 

     几何图形中,该第三点P3可以在P1和P2之间画一条线来确定。这条直线恰好与椭圆曲线相交于另外一个地方。此 点记为 P3'= (x,y)。然后,在x轴做翻折获得 P3=(x,-y)。
    下面是几个可以解释“穷远点”之存在需要的特殊情况。 若 P1和 P2是同一点,P1和P2间的连线则为点P1 的切线。曲线上有且只有一个新的点与该切线相交。该切线的斜 率可用微积分求得。即使限制曲线点为两个整数坐标也可求得斜率!
在某些情况下(即,如果P1和P2具有相同的x值,但不同的y值),则切线会完全垂直,在这种情况下,P3 = “无穷 远点”。 

     若P1就是“无穷远点”,那么其和 P1 + P2= P2。类似地,当P2是无穷远点,则P1+ P2 = P1。这就是把无穷远点类似 于0的作用。 事实证明,在这里 + 运算符遵守结合律,这意味着(A+B)+C = A+(B+C)。这就是说我们可以直接不加 括号书写 A + B + C,而不至于混淆。 因此,我们已经定义了椭圆加法,我们可以对乘法用拓展加法的标准方法进 行定义。给定椭圆曲线上的点P,如果k是整数,则 kP = P + P + P + …+ P(k次)。注意,在这种情况下k有时被混 淆而称为“指数”。 

 生成公钥 

    以一个随机生成的私钥k为起点,我们将其与曲线上预定的生成点G相乘以获得曲线上的另一点,也就是相应的公钥 K。生成点是secp256k1标准的一部分,比特币密钥的生成点都是相同的: {K = k * G}

    其中k是私钥,G是生成点,在该曲线上所得的点K是公钥。因为所有比特币用户的生成点是相同的,一个私钥k乘 以G将 得到相同的公钥K。k和K之间的关系是固定的,但只能单向运算,即从k得到K。这就是可以把比特币地址 (K的衍生) 与任何人共享而不会泄露私钥(k)的原因。 提示 因为其中的数学运算是单向的,所以私钥可以转换为公钥,但公钥不能转换回私钥。

为实现椭圆曲线乘法,我们 以之前产生的私钥k和与生成点G相乘得到公钥K: 

K = 1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD * G 

公钥K 被定义为一 个点 K = (x, y):

K = (x, y) 其中, 

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A 

y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

     为了展示整数点的乘法,我们将使用较为简单的实数范围的椭圆曲线。请记住,其中的数学原理是相同的。我们的 目标是找到生成点G的倍数kG。也就是将G相加k次。在椭圆曲线中,点的相加等同于从该点画切线找到与曲线相交 的另一 点,然后翻折到x轴。下图显示了在曲线上得到 G、2G、4G 的几何操作。

比特币的密码学_第5张图片

提示大多数比特币程序使用OpenSSL加密库进行椭圆曲线计算。例如,调用EC_POINT_mul() 函数,可计算得到公 钥。 

 比特币地址 

    比特币地址是一个由数字和字母组成的字符串,可以与任何想给你比特币的人分享。由公钥(一个同样由数字和字 母组 成的字符串)生成的比特币地址以数字“1”开头。下面是一个比特币地址的例子: 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
在交易中,比特币地址通常以收款方出现。如果把比特币交易比作一张支票,比特币地址就是收款人,也就是我们 要写入收款人一栏的内容。一张支票的 收款人可能是某个银行账户,也可能是某个公司、机构,甚至是现金支票。 支票不需要指定一个特定的账户,而是用一个抽象的名字作为收款人,这使它成为一种相当灵活的支付工具。
    与此类似,比特币地址使用类似的抽象,也使比特币交易变得很灵活。比特币地址可以代表一对公钥和私钥的所有 者,也可以代表其 它东西,比如会在后面的“P2SH (Pay-to-Script-Hash)”一节讲到的付款脚本。现在,让我们来看 一个简单的例子,由公钥生成比特币地址。

    比特币地址可由公钥经过单向的加密哈希算法得到。哈希算法是一种单向函数,接收任意长度的输入产生指纹或哈 希。加密哈希函数在比特币中被广泛使用 :比特币地址、脚本地址以及在挖矿中的工作量证明算法。由公钥生成比 特币地址时使用的算法是Secure Hash Algorithm (SHA)和the RACE Integ rity Primitives Evaluation Message Digest (RIPEMD),具体地说是SHA256和RIPEMD160。

   以公钥 K 为输入,计算其SHA256哈希值,并以此结果计算RIPEMD160 哈希值,得到一个长度为160位(20字 节)的数字: A = RIPEMD160(SHA256(K)) 公式中,K是公钥,A是生成的比特币地址。提示比特币地址与公钥不同。比特币地址是由公钥经过单向的哈希函数生成的。

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

    下图描述了如何从公钥生成比特币地址

比特币的密码学_第6张图片

 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的数据就是无效的。一 个错误比特币地址就不会被钱包软件认为是有效的地址,否则这种错误会造成资金的丢失。

    为了将数据(数字)转换成Base58Check格式,首先我们要对数据添加一个称作“版本字节”的前,这个前用来 识别编码的数据的类 型。例如,比特币地址的前是0(十六进制是0x00),而对私钥编码时前是128(十六进 制是0x80)。 表4-1会列出一些常见版本的前。

    接下来,我们计算“双哈希”校验和,意味着要对之前的结果(前和数据)运行两次SHA256哈希算法: checksum = SHA256(SHA256(prefix+data)) 在产生的长32个字节的哈希值(两次哈希运算)中,我们只取前4个字节。这4个字节就作为检验错误的代码或者 校验和。校验码会添加到数据之后。

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

  比特币的密码学_第7张图片

    在比特币中,大多数需要向用户展示的数据都使用Base58Check编码,可以实现数据压缩,易读而且有错误检验。 Base58Check编码中的版本前是用来创造易于辨别的格式,在Base58里的格式在Base58Check编码的有效载荷 的开始包含了明确的属性。这些属性使用户可以 轻松明确被编码的数据的类型以及如何使用它们。例如我们可以看 到他们的不同,Base58Check编码的比特币地址是以1开头的,而Base58Check编码的私钥WIF是以5开头的。表41展示了一些版本前和他们对应的Base58格式。

    表4-1 Base58Check版本前和编码后的结果

比特币的密码学_第8张图片

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

例4-3.从私钥中创建Base58Check编码的比特币地址 link:code/addr.cpp

代码使用预定义的私钥在每次运行时产生相同的比特币地址,如下例所示 例4-3.编译并运行addr代码 

$ g++ -o addr addr.cpp (pkg-config --cflags --libs libbitcoin)

$ ./addr 

Public key: 0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa 

Address: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK 

 密钥的格式 

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

私钥的格式 

    私钥可以以许多不同的格式表示,所有这些都对应于相同的256位的数字。表4-2展示了私钥的三种常见格式。不同 的格式用在不同的场景下。十六进制和原始的二进制格式用在软件的内部,很少展示给用户看。WIF格式用在钱包 之间密钥的输入和输出,也用于代表私钥的二维码(条形码)。

比特币的密码学_第9张图片

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

比特币的密码学_第10张图片

这些表示法都是用来表示相同的数字、相同的私钥的不同方法。虽然编码后的字符串看起来不同,但不同的格式彼 此之 间可以很容易地相互转换。请注意,“raw binary”未显示在表4-3 示例中,根据定义此处显示的任何编码的格 式,不是raw binary数据。 我们使用Bitcoin Explorer中的wif-to-ec命令(请参阅[appdx_bx])来显示两个WIF键代表相同的私钥:

 $ bx wif-to-ec 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd 

$ bx wif-to-ec KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd 

从Base58Check解码 

Bitcoin Explorer命令(参见[appdx_bx])使得编写shell脚本和命令行“管道”变得容易,这些方式可以处理比特币 密钥,地址和交易。 您可以使用Bitcoin Explorer在命令行上解码Base58Check格式。 我们使用base58check-decode命令解码未压缩的密钥: 

$ bx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn wrapper {


} 结果包含密钥作为有效载荷,WIF版本前128和校验和。 请注意,压缩密钥的“有效负载”附加了后01,表示导 出的公钥要压缩: $ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ wrapper {


}
Base58Check 要转换成Base58Check(与上一个命令相反),我们使用Bitcoin Explorer的 base58check-encode命令(请参阅[appdx_bx]),并提供十六进制私钥,其次是WIF版本前128:

bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd -version 128 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

要将压缩格式的私钥编码为Base58Check(参见“压缩格式私钥”一节),我们需在十六进制私钥的后面添加后 01,然后使用跟上面一样的方法: 

$ bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 -version 128

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ 

生成的WIF压缩格式的私钥以字母“K”开头,用以表明被编码的私钥有一个后“01”,且该私钥只能被用于生成压缩 格式 的公钥(参见“压缩格式公钥”一节)。 

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

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

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A 

y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB 

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

K = 

04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C60 0B96A72BBC4749D5141C90EC8AC328AE 52DDFE2E505BDB 

压缩格式公钥 

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

    正如我们在“4.1.4 公钥”一节所见,一个公钥是一个椭圆曲线上的点(x, y)。而椭圆曲线实际是一个数学方程,曲线 上的点实际是该方程的一个解。因此,如果我们知道了公钥的x坐标,就可以通过解方程y2 mod p = (x3 + 7) 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坐 标,从而将公钥解压缩为在椭圆曲线上的完整的点坐标。下图阐释了公钥压缩:

比特币的密码学_第11张图片

    下面是前述章节所生成的公钥,使用了264比特(66个十六进制数字)的压缩格式公钥格式,其中前03表示y坐 标是一个奇数:
      K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
    这个压缩格式公钥对应着同样的一个私钥,这意味它是由同样的私钥所生成。但是压缩格式公钥和非压缩格式公钥 看起来不同。更重要的是,如果我们使用双哈希函数(RIPEMD160(SHA256(K)))将压缩格式公钥转化成比特币地 址,得到的地址将会不同于由非压缩格式公钥产生的地址。这种结果会让人迷惑,因为一个私钥可以生成两种不同 格式的公钥——压缩格式和非压缩格式,而这两种格式的公钥可以生成两个不同的比特币地址。但是,这两个不同 的比特币地址的私钥是一样的。

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

    为了解决这个问题,当私钥从钱包中被导出时,代表私钥的WIF在较新的比特币钱包里被处理的方式不同,表明该 私钥已经被用来生成压缩的公钥和因此压缩的比特币地址。这个方案可以解决导入私钥来自于老钱包还是新钱包的 问题,同时也解决了通过公钥生成的比特币地址是来自于压缩格式公钥还是非压缩格式公钥的问题。后新钱包在 扫描区块链时,就可以使用对应的比特币地址去查找该比特币地址在区块链里所发生的交易。我们将在下一节详细 解释这种机制是如何工作的。 

压缩格式私钥 

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

比特币的密码学_第12张图片

    请注意,十六进制压缩私钥格式在末尾有一个额外的字节(十六进制为01)。 虽然Base58编码版本前对于WIF 和WIF压缩格式都是相同的(0x80),但在数字末尾添加一个字节会导致Base58编码的第一个字符从5变为K或 L,考虑到对于Base58这是十进制编码100号和99号之间的差别。对于100是一个数字长于99的数字,它有一个前 1,而不是前9。当长度变化 ,它会影响前。 在Base58中,前5改变为K或L,因为数字的长度增加一个字 节。

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

    如果一个比特币钱包实现了压缩格式公钥,那么它将会在所有交易中使用该压格式缩公钥。钱包中的私钥将会被用 来在曲线上生成公钥点,这个公钥点将会被压缩。压缩格式公钥然后被用来生成交易中使用的比特币地址。当从一 个实现了压缩格式公钥的新的比特币钱包导出私钥时,钱包导入格式(WIF)将会被修改为WIF压缩格式,该格式 将会在私钥的后面附加一个字节大小的后01。 终的Base58Check编码格式的私钥被称作WIF(“压缩”)私钥, 以字母“K”或“L”开头。而以“5”开头的是从较老的钱包中 以WIF(非压缩)格式导出的私钥。 提示 “压缩格式私钥”是一个不当用词!私钥不是压缩的。WIF压缩格式的私钥只是用来表明他们只能被生成压缩的 公钥和对应的比特币地址。相反地,“WIF压缩”编码的私钥还多出一个字节,因为这种私钥多了后“01”。该后 是用来区分“非压缩格式”私钥和“压缩格式”私钥。 

高级密钥和地址 

在以下部分中,我们将看到高级形式的密钥和地址,诸如加密私钥、脚本和多重签名地址,靓号地址,和纸钱包。 

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

    BIP0038提出了一个通用标准,使用一个口令加密私钥并使用Base58Check对加密的私钥进行编码,这样加密的私 钥就可以安全地保存在备份介质里,安全地在钱包间传输,保持密钥在任何可能被暴露情况下的安全性。这个加密 标准使 用了AES,这个标准由NIST建立,并广泛应用于商业和军事应用的数据加密。 BIP0038加密方案是:输入一个比特币私钥,通常使用WIF编码过,base58chek字符串的前“5”。此外BIP0038加 密方案需要一个长密码作为口令,通常由多个单词或一段复杂的数字字母字符串组成。BIP0038加密方案的结果是 一个由 base58check编码过的加密私钥,前为6P。如果你看到一个6P开头的的密钥,这就意味着该密钥是被加 密过,并需要一个口令来转换(解码)该密钥回到可被用在任何钱包WIF格式的私钥(前为5)。许多钱包APP现 在能够识别 BIP0038加密过的私钥,会要求用户提供口令解码并导入密钥。第三方APP,诸如非常好用基于浏览器 的Bit Address , 可以被用来解码BIP00038的密钥。

     通常使用BIP0038加密的密钥用例是纸钱包——一张纸张上备份私钥。只要用户选择了强口令,使用BIP0038加 密的私钥的纸钱包就无比的安全,这也是一种很棒的比特币离线存储方式(也被称作“冷存储”)。 在 bitaddress.org上测试表4-5中加密密钥,看看如何输入密码以得到加密密钥。 表4-5 BIP0038加密私钥例子

比特币的密码学_第13张图片

 P2SH (Pay-to-Script Hash)和多重签名地址 
     正如我们所知,传统的比特币地址从数字1开头,来源于公钥,而公钥来源于私钥。虽然任何人都可以将比特币发 送到 一个1开头的地址,但比特币只能在通过相应的私钥签名和公钥哈希值后才能消费。
以数字3开头的比特币地址是P2SH地址,有时被错误的称谓多重签名或多重签名地址。他们指定比特币交易中受益 人为哈希的脚本,而不是公钥的所有者。这个特性在2012年1月由BIP0016引进,目前因为BIP0016提供了增加功 能到地址本身的机会而被广泛的采纳。不同于P2PKH交易发送资金到传统1开头的比特币地址,资金被发送到3开 头的地址时,需要的不仅仅是一个公钥的哈希值和一个私钥签名作为所有者证明。在创建地址的时候,这些要求会 被指定在脚本中,所有对地址的输入都会被这些要求阻隔。
    一个P2SH地址从交易脚本中创建,它定义谁能消耗这个交易输出(后面“P2SH(Pay-to-Script-Hash)”一节对此有 详细的介绍)。编码一个P2SH地址涉及使用一个在创建比特币地址用到过的双重哈希函数,并且只能应用在脚本 而不是公钥:
      script hash = RIPEMD160(SHA256(script))

    产生的脚本哈希由Base58Check编码前为5的版本、编码后得到开头为3的编码地址。一个P2SH地址例子是 3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM。可以使用Bitcoin Explorer命令脚本编码获得,比如sha256, ripemd160, and base58check-encode,举例如下: $ echo dup hash160 [ 89abcdefabbaabbaabbaabbaabbaabbaabbaabba ] equalverify checksig > script $ bx script-encode < script | bx sha256 | bx ripemd160 | bx base58check-encode --version 5 3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM

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

 多重签名地址和P2SH 

  目前,P2SH函数常见的实现是多重签名地址脚本。顾名思义,底层脚本需要多个签名来证明所有权,此后才能 消费资金。设计比特币多重签名特性是需要从总共N个密钥中需要M个签名(也被称为“阈值”),被称为M-N多签 名,其 中M是等于或小于N。例如,第一章中提到的咖啡店主Bob使用多重签名地址需要1-2签名,一个是属于他的 密钥和一个属于他同伴的密钥,以确保其中一方可以签署消费一笔锁定到这个地址的输出。这类似于传统的银行中 的一个“联合账户”,其中任何一方配偶可以单独签单消费。或就像Bob雇佣的网页设计师Gopesh, 创立一个网 站,可能为他的业务需要一个2-3的多签名地址,确保除非至少两个业务合作伙伴签署签名交易才可以进行支付消 费。

比特币靓号地址 

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

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

    

你可能感兴趣的:(区块链)