比特币开发者指南(6)--钱包

钱包

比特币钱包可以指代钱包程序或钱包文件。电子钱包程序创建公共密钥接收satoshis,并使用相应的私钥花费satoshis 。电子钱包文件存储私钥和(可选的)与钱包程序的交易相关的其他信息。

钱包程序和钱包文件在下面的不同小节中介绍,本文档试图澄清我们在谈论是钱包程序或钱包文件。

钱包软件

允许satoshis的接收和支出是钱包软件的唯一基本特征,但是特定的钱包程序不需都要做这两件事情。两个钱包程序可以一起工作,一个程序分发公钥以便接收satoshis,另一个程序签名交易来花费那些 satoshis。

钱包程序还需要与P2P网络进行交互,以从块链获取信息并广播新的交易。然而,分发公钥或签名交易的程序就不需要与P2P网络本身进行交互。

这使我们有一个钱包系统的三个必要但可分离的部分:一个公钥分发程序,签名程序和联网程序。在下面的小节中,我们将介绍这些部分的常见组合。

注意:我们讲通常是分发公钥。在许多情况下,将分发P2PKH或P2SH哈希来代替公钥,实际的公钥仅在它们控制的输出被花费时候分发。

全服务的钱包

最简单的钱包是一个执行所有三个功能的程序:它产生私钥,导出相应的公钥,根据需要帮助分发公钥,监视花费在公钥上的输出,创建并签名花费那些输出的交易,并广播签名过的交易。


Full-Service Wallets

在撰写本文时,几乎所有流行的钱包可用作全服务钱包。

全方位服务钱包的主要优点是易于使用。一个程序执行用户需要接收的所有东西,并花费satoshis。

全服务钱包的主要缺点是它们在连接到互联网的设备上存储私钥。这种设备被入侵是常见的事情,因特网连接使得容易将私钥从被感染的设备发送给攻击者。

为了防盗,许多钱包程序为用户提供可选项加密包含私钥的钱包文件。当私钥不被使用时,它可以提供保护,但是它不能防止捕获密钥的攻击或从内存中读取解密密钥。

只签名钱包

为了提高安全性,可以通过在更安全的环境中运行的单独的钱包程序来生成和存储私钥。这些仅签名的钱包与与对等网络交互的网络钱包配合使用。

典型的只签名钱包软件使用确定的密钥产生器(稍后描述)去创建父私钥公钥,它们可以创建新的子私钥公钥。


Signing-Only Wallets

首次运行时,只签名的钱包创建一个父私钥,并将相应的父公钥传送到联网的钱包。

联网的钱包使用父公钥导出子公钥,可选的帮助分发它们,或者监控花费在那些公钥上的输出,为花费那些输出创建未签名的交易,并传输未签名的交易到只签名钱包。

通常用户使用只签名钱包有机会看到未签名交易的细节特别是输出细节。

在可选的检查步骤之后,只签名钱包使用父私钥导出合适的子私钥,并签名那些交易,发送签名过的交易给联网的钱包。

然后联网的钱包将签名过的交易广播到P2P网络上。

以下小节描述了只签名钱包的两个最常见的变体:离线钱包和硬件钱包。

离线钱包

一些完整的全服务钱包程序可以作为两种独立的钱包运行:一个程序实例作为只签名钱包(通常称为离线钱包),另外一个程序实例联网钱包(通常称为在线钱包或只观看钱包)。

离线钱包如此命名,因为它打算在不连接到任何网络的设备上运行,大大减少了攻击次数。如果是这种情况,通常由用户使用可移动介质(如USB驱动器)处理所有数据传输。用户的工作流程如下:
  • (离线)禁用设备上的所有网络连接,并安装钱包软件。在离线模式下启动钱包软件,以创建父私钥和公钥。将父公钥复制到可移动媒体。
  • (在线)将钱包软件安装在另一台设备上,该设备联网,并从可移动媒体导入父公钥。像使用全服务钱包一样,分发公钥来接收付款。当准备花费satoshis时,填写输出细节,并将钱包生成的未签名交易保存到可移动媒体。
  • (离线)在脱机实例中打开未签名的事务,查看输出详细信息,确保他们将正确的金额用于正确的地址。这可以防止在线钱包上的恶意软件欺骗用户签署支付给攻击者的交易。经过审查,将交易签名并将其保存到可移动媒体上。
  • (在线)在在线实例中打开签名的交易,以便将其广播到P2P网络。
脱机钱包的主要优点是可以大大提高全服务钱包的安全性。只要离线钱包不会受到损害(或有缺陷),用户在签署之前审核所有外发交易,即使在线钱包被破坏,用户的satoshis也是安全的。

离线钱包的主要缺点是麻烦。为了安全最大化,它们要求用户仅将一个设备专用于离线任务。离线钱包必须在资金花费时启动,用户必须将数据在在线设备和离线设备中来回复制。

硬件钱包

硬件钱包是专用于运行只签名钱包的设备。他们的奉献使他们能够消除设计用于一般用途的操作系统中存在的许多漏洞,从而允许他们直接与其他设备通信,以便用户不需要手动传输数据。用户的工作流程如下:

  • (硬件)创建父私钥和公钥。将硬件钱包连接到网络设备,以便它可以获取父公钥。
  • (网络)正如使用全服务钱包一样,分发公钥来接收付款。当准备花费satoshis时,填写交易详情,连接硬件钱包,然后点击支出。联网的钱包将自动发送交易详细信息到硬件钱包。
  • (硬件)在硬件钱包的屏幕上查看交易详情。某些硬件钱包可能会提示输入密码或PIN码。硬件钱包将事务签名并上传到联网的钱包。
  • (网络化)联网的钱包从硬件钱包接收签名的交易,并将其广播到网络。

硬件钱包的主要优点是可以大大提高全服务钱包的安全性,而且不需要比离线钱包更麻烦。

硬件钱包的主要缺点是麻烦。即使麻烦小于离线钱包,用户仍然必须购买硬件钱包设备,并在他们需要使用签名钱包签名交易时候随身携带。

一个额外的(希望是暂时的)缺点是,在撰写本文时,流行的钱包程序很少支持硬件钱包 - 虽然几乎所有流行的钱包程序已宣布有意外去至少支持一种硬件钱包型号。

仅分发钱包

钱包程序在难以安全的环境中运行(如Web服务器)可以设计为只分发公钥(包括P2PKH或P2SH地址 )而没有更多功能。设计这些极简钱包有两种常见的方法:


Distributing-Only Wallets

使用多个公开密钥或地址预填充数据库,然后使用其中一个数据库条目根据请求分发pubkey脚本或地址。为了避免密钥重用,Web服务器应该跟踪使用的密钥,永远不会用完公钥。通过使用父公钥,可以使其更容易,如下一个方法中所建议的那样。

使用父公钥创建子公钥。为了避免密钥重用,必须使用一种方法来确保相同的公钥不分发两次。这可以是分配的每个密钥的数据库条目或指向密钥索引号码的递增指针。

这两种方法都不会增加大量的开销,特别是如果数据库被用于关联输入的付款和一个单独的公钥来跟踪付款的时候。有关详细信息,请参阅支付流程部分。

钱包文件

比特币钱包核心就是私钥的集合。这些集合以数字方式存储在文件中,甚至可以物理存储在纸张上。

私钥格式

私钥是用于从特定的地址解锁satoshis。在比特币中,标准格式的私钥只是一个256位数字,在值的范围是:
0x01 and 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140, 几乎覆盖 2256-1个数值的整个范围.该范围由比特币使用的secp256k1 ECDSA加密标准决定。

钱包导入格式(WIF)


为了使私钥的复制不容易出错,可以使用钱包导入格式WIF。WIF在私钥上使用base58Check编码,大大降低了复制错误的几率,这和标准比特币地址很类似 。

  1. 拿出一个私钥。
  2. 在mainnet 地址之前添加一个0x80字节或testnet 地址添加一个0xef字节。
  3. 如果它与压缩的公钥一起使用(在后面的小节中描述),则会添加一个0x01字节。如果与未压缩的公钥一起使用,则不附加任何内容。
  4. 在扩展密钥上执行SHA-256哈希。
  5. 对SHA-256哈希的结果执行SHA-256哈希。
  6. 取第二个SHA-256哈希的前四个字节;这是校验和。
  7. 从第2点的扩展密钥的末尾添加第5点的四个校验和字节。
  8. 使用Base58Check编码将结果从字节字符串转换为Base58字符串。

该过程很容易可逆,使用Base58解码功能,并删除填充。


迷你私钥格式

Mini 私钥格式是一种用于在30个字符内编码私钥的方法,可以将密钥嵌入在小物理空间中,例如物理比特币令牌,抗损害的QR码。

  1. 迷你密钥的第一个字符是'S'。
  2. 为了确定迷你私钥是否格式正确,在私钥中添加了一个问号。
  3. 计算SHA256哈希。如果产生的第一个字节为“00”,则格式良好。这个关键的限制是错误检测机制。用户使用随机数强制穷举,直到产生格式正确的迷你私钥。
  4. 为了导出完整的私钥,用户只需要使用原始迷你私钥的单个SHA256哈希。这个过程是单向的:从派生密钥很难计算出迷你私钥格式。
由于与'l'的视觉相似性,许多实现不允许迷你私钥中使用字符'1'。

资源:创建和兑现这些密钥的常用工具是Casascius Bitcoin Address Utility。


公钥格式

比特币ECDSA公钥表示在secp256k1中定义的特定椭圆曲线(EC)上的一个点。在其传统的未压缩形式中,公钥包含一个标识字节,一个32字节的X坐标和一个32字节的Y坐标。下面非常简化的图表显示了比特币在连续数字字段上使用的椭圆曲线y 2 = x 3 + 7上的这一点。

Point On ECDSA Curve

(Secp256k1实际上是一个大素数的modulos坐标,尽管原理是相同的,但是它产生非连续整数的字段和明显不太清楚的图。

通过去除Y坐标,可以实现公钥大小几乎减少50%,而不会改变任何基本法则。这是可能的,因为沿曲线上只有两个点共享任何特定的X坐标,因此32位Y坐标可以用一个位来代替,表示点在图示中出现的是顶部还是底部。

创建这些压缩的公钥不会丢失任何数据 - 只需要少量的CPU来重建Y坐标和访问未压缩的公钥。官方的secp256k1文档中描述了未压缩和压缩的公钥,并在广泛使用的OpenSSL库中默认支持。

因为他们易于使用,并减少了存储每次花费输出的将近一半的链空间,压缩公钥是Bitcoin Core默认的,并是所有比特币软件推荐使用的。

然而,Bitcoin Core在0.6之前使用未压缩的密钥。这造成一些麻烦,因为非压缩密钥的哈希格式不同于压缩密钥的哈希格式,所以同一个密钥要有两个不同的P2PKH地址。这也意味着密钥必须在签名脚本中以正确的格式提交,能匹配上一个先前输出的 pubkey脚本中的哈希。

因此,Bitcoin Core使用几个不同的标识符字节来帮助程序识别如何使用密钥:
  • 私钥意味着在被Base-58编码前使用添加了0x01的压缩公钥。(参见上面的私钥编码部分。)
  • 未压缩的公钥以0x04开头; 压缩的公开密钥以0x03或0x02开头,取决于它们是否大于或小于曲线的中点。这些前缀字节都在官方的secp256k1文档中定义使用。

分层确定性密钥创建


分层确定性密钥创建和传输协议(HD协议)极大地简化了钱包的备份,无需相同钱包的多个程序之间重复通信,允许创建可以独立运作的子帐户,即使子帐户遭到入侵,也能使每个父账户监控或控制其子帐户,并将每个账户划分为完全访问和受限访问部分,可以允许不受信任的用户或程序接收或监视付款但不能花费它们。

HD协议利用ECDSA公钥创建函数point(),其占用一个大整数(私钥),并将其转换为图形点(即公钥):


以point()的工作方式,可以通过将现有的父公钥和另外一个从整数值(i)创建的公钥组合起来创建子公钥。这个子公钥与初始父私钥加上i值除去所有Bitcoin软件使用的全局常量p的产生的余数经过point()函数而创建的公钥相同。

这意味着两个或多个独立的程序使用一致的整数序列可以从单个父键对创建一系列唯一的子键,而无需任何进一步的通信。此外,分发新公钥的程序可以不得到私钥而接收付款,这样允许公钥分发器工作在一个可能不安全的平台比如公共Web服务器。

子公钥还能通过重复子键推导操作来创建他们自己的子公钥(孙子公钥)。


无论是创建子公钥还是进一步派生的公钥,可预测的整数序列将不如对所有交易使用单个公钥,因为知道一个子公钥的任何人可以找到从相同的父公钥创建的所有其他子公钥。相反,可以使用随机种子来确定性地生成整数序列,使得子公钥之间的关系对于没有该种子的任何人都是不可见的。

HD协议使用单个根种子创建具有不可推导的整数值的子,孙和其他派生密钥。每个子密钥还可以从它的父密钥获取一个确定性产生的种子,称为链代码,所以一个链代码的损害不会影响整个层次的整数序列,允许主链代码即使例如基于Web的公钥分发器被hacked还能继续使用。


Overview Of Hierarchical Deterministic Key Derivation
  • 如上所述,HD密钥导出需要四个输入:
  • 父私钥和父公钥是常规的未压缩256位ECDSA密钥。
  • 父链代码是256位的看似随机数据。
  • 索引号是程序指定的32位整数。
在上述图示中所示的正常格式中,将父链代码,父公钥和索引号馈送到单向加密散列(HMAC-SHA512),以产生512位的确定性生成但似乎随机的数据。哈希输出右侧的看似随机的256位用作新的子链代码。使用散列输出的左侧的看似随机的256位作为与父私钥或父公共密钥组合的整数值分别创建子私钥或子公钥:

指定不同的索引号将从相同的父键创建不同的不可链接的子键。使用子链代码重复子键的过程将创建不可链接的孙子键。

因为创建子键需要键和链代码,所以键和链代码一起被称为扩展键。扩展私钥及其相应的扩展公钥具有相同的链码。(顶级父级)主密钥和主链码从随机数据派生,如下所示。


Creating A Root Extended Key Pair

根据128位,256位或512位随机数据​​创建一个根种子。这个少于128位的根种子是用户需要备份的唯一数据,以便使用特定设置来导出由特定钱包程序创建的每个密钥。

Warning icon 警告:在撰写本文时,HD钱包程序预计不会完全兼容,因此用户只能使用相同的HD钱包程序,具有与特定根种子相同的HD相关设置。

根种子被散列以创建512位的看似随机数据,从其中创建主专用密钥和主链码 ,主扩展私钥)。使用point()从主密钥派生主公钥,其中连同主链码,是主扩展公钥。主扩展键在功能上等同于其他扩展键;只有他们的位置在层次结构的顶部,使他们特别。


固化密钥

固化扩展密钥修复了普通扩展密钥的潜在问题。如果攻击者获得正常的父链接代码和父公开密钥,他可以强制所有从其派生的链码。如果攻击者也获得了一个子孙,孙子或进一步的私钥,他可以使用链码生成所有扩展的私钥从私钥下降,如下图所示的孙子孙子孙代。


Cross-Generational Key Compromise

也许更糟的是,攻击者可以反转正常的子私钥导出公式,并从子私钥中减去父链代码来恢复父私钥,如上图所示的子代和父代。这意味着从其中获取扩展的公钥和任何私钥的攻击者可以恢复公钥的 私钥和所有键从它下降。

因此,扩展公开密钥的链码应该比标准的公钥更好地保护,并且应该建议用户不要导出甚至非扩展的私钥到可能不可信的环境。

这可以通过用强化的密钥导出公式替换正常密钥推导公式来进行一些折衷。

上述部分中描述的正常密钥导出公式将索引号父链代码和父公开组合在一起,以创建子链代码和与父私钥组合的整数值来创建子私钥。


Creating Child Public Keys From An Extended Private Key

如上所示的硬化公式将索引号父链代码和父私钥组合在一起,以创建用于生成子链的数据代码和子私钥。该公式使得无法创建子公共密钥,而不知道父私钥。换句话说,父扩展公钥不能创建硬化的子公共密钥。

因此,固化扩展私钥比正常的扩展私钥或更无用,但是固化扩展私钥在多层密钥导出之间创建防火墙保证损害不能发生。因为硬化子扩展公钥不能自己生成孙子链码,父扩展公钥的妥协不能与妥协的孙子私钥来创建孙孙扩展私钥。

HD协议使用不同的索引号来指示是否应生成正常或硬化的密钥。索引号从0x00到0x7fffffff(0到2 31 -1)将产生正常的键;索引号从0x80000000到0xffffffff将生成一个硬化密钥。为了简化说明,许多开发人员使用素数符号表示硬化密钥,因此第一个正常密钥(0x00)为0,第一个硬化密钥(0x80000000)为0。

(Bitcoin开发人员通常使用ASCII撇号而不是unicode主要符号,这是我们以后的惯例)

该紧凑描述进一步与由m或M前缀的斜杠组合,以指示层次结构和键类型,其中m是私钥和M是公钥。例如,m / 0'/ 0/122'是指主私钥的第一个硬化子节点(按索引)的第一个正常子节点(按索引)的第123个硬化私有子节点(按索引号)。以下层次结构说明了主要符号和强化的关键防火墙。


Example HD Wallet Tree Using Prime Notation

遵循BIP32 HD协议的钱包只能创建主私钥m的固化子密钥,以防止受损的子键损害主键。由于主密钥没有正常的孩子,所以在HD钱包中不使用主公钥。所有其他键可以有普通的孩子,所以可以使用相应的扩展的公钥。

HD协议还描述了扩展公钥和扩展私钥的序列化格式。有关详细信息,请参阅开发者参考的钱包部分或BIP32中完整的HD协议规范。

储存根种子

HD协议中的根种子是必须精确备份的128,256或512位随机数据。为了更方便地使用诸如存储或手工复制的非数字备份方法,BIP39定义了一种从伪伪建立512位根种子的方法普通自然语言词汇(助记符)本身是由128到256位的熵创建的,并且可选地受密码保护。

所产生的词数与所用熵的数量有关:

密码短语可以是任何长度的。它只是附加到助记符伪句子,然后使用HMAC-SHA512将助记符和密码两次散列2,048次,从而产生一个看似随机的512位种子。因为散列函数的任何输入都会创建一个看似随机的512位种子,所以没有一个基本的方式来证明用户输入正确的密码,可能允许用户保护种子即使在胁迫下。

有关实现细节,请参阅BIP39。


松散密钥钱包

也称为“Just a Bunch Of Keys(JBOK)”的Loose-Key钱包是来自Bitcoin Core客户端钱包的一种形式 。比特币核心客户端钱包将通过伪随机数生成器(PRNG)自动创建100 私钥 / 公钥,以供以后使用。

这些未使用的私钥存储在虚拟“密钥池”中,每当使用以前生成的密钥时,将生成新密钥,确保池保持100个未使用的密钥。(如果钱包被加密,则仅在钱包被解锁时才产生新密钥。)

考虑到备份必须手动运行以保存新生成的私钥,这在备份密钥方面造成了相当大的困难。如果在备份之前生成,使用,然后丢失新的密钥对集,则存储的satoshis可能永远丢失。许多旧式移动钱包遵循类似的格式,但只有根据用户需求生成新的私钥。

由于备份麻烦,此钱包类型正在被积极淘汰,不鼓励使用。

没有更多推荐了,返回首页