钱包 Wallets
比特币钱包的实质是私钥收集器。这些私钥被存到一个文件中,甚至可以打印到一片纸上。
Private Key Formats 私钥格式
私钥是用于从一个特定的地址上解锁出satoshis。在比特币技术体系中,一个私钥的标准格式是一个256位的数字, 在下面的数值之间:
0x1 and 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141, 展示了几乎整个 2256-1范围区间。此范围区间是由secp256k1 ECDSA 指定的,比特币技术体系使用这个加密标准。
Wallet Import Format (WIF) 导入钱包的格式
为了在复制的私钥时不易发生错误,可以使用导入钱包的格式。WIF使用base58Check编码私钥, 大大减少错误复制的可能性, 就像标准的比特币地址。
- 私钥。
- 在若是主网添加0 x80字节,或测试网络则为0xef 。
- 对延展键执行sha – 256哈希。
- 在前述结果上再执行一次sha - 256哈希。
- 第二次哈希值的首个四字节,作为校验值
- 将第5点获得的校验值添加到第2点所指的扩展键上。
- 使用Base58Checkencoding方法将字符串转换成使用Base58编码的字符串。
使用Base58解码功能很容易使过程可逆,并且去除填充。
Mini Private Key Format 迷你私钥格式
迷你私钥格式是一个使用30个字符编码私钥的方法, 从而容易把私钥嵌入在一个小的物理空间中,如实体比特币的令牌,以及抗刮伤的二维QR码上。
- 迷你私钥的第一个字符是“S”。
- 为了确保一个迷你私钥被正确格式,先将一个问号添加到私钥。
- 计算SHA256散列。如果产生的第一个字节是“00”,这是格式良好的。用这个机制来避免打字失败。在暴力破解过程中使用随机数,直到一个具备正确格式的迷你私钥产生。
- 为了获得完整的私有密钥,用户只需要一个SHA256散列的原始迷你私钥。这个过程是单向的:从衍生的秘钥计算迷你私有密钥是棘手的。
由于其视觉相似“l”, 很多实现不允许的字符' 1 '的迷你私钥。
资源: 常见创建和赎回这些键的工具是Casascius实用程序。
Hierarchical Deterministic Key Creation 创建层次确定的钥匙
等级确定的钥匙创建和传输协议(HD协议)极大地简化了钱包备份,不需要在使用同一钱包的多个程序之间的重复通信,允许创建可以独立运作的子账户,即使孩子帐户被破解了 父账户依然可以监控或控制子账户的能力,并且可以把账户分为“全部权限”或者“限制访问”的,不受信任的用户或程序可以被允许接收或监控支付但不能够花出比特币。
HD协议利用ECDSA协议的公钥创建函数 point() ,将一个较大的整数(也就是私钥),转变成一个图点(即公钥):
point(private_key) == public_key
基于point()函数的运行方式,可以通过结合已有的父公钥和创建自任意整数的公钥,创建一个子公钥。如果添加整数i到最初(父)私钥,由point()创建的这个子公钥是相同的, 然后发现剩下的总和除以一个所有比特币软件使用的常量G(译者注:这个章节翻译的不好)
point( (parent_private_key + i) % G ) == parent_public_key + point(i)
当基于同一个整数序列后,即使不再交流,两个或两个以上的单独程序,可以从一个父公私钥匙对上创建出一系列的惟一的子密钥。此外,在不用存取私钥的情况下,用来收款的公钥可以分发出去,从而允许在一个可能不安全的公共WEB服务器上分发公钥。
通过重复子密钥生成操作,子公钥还可以创建自己的公钥(孙公钥):
point( (child_private_key + i) % G ) == child_public_key + point(i)
无论是否生成子公钥或更一步生成子公钥,一个可预测的整数序列值不会比使用对所有交易使用单一公钥更好,尤其是当知晓一个子公钥就能找到的所有从同一父公钥生成的子公钥。其实,基于一个随机种子生成的整数序列,此种方式生成子公钥互不可见的,除非知道这个种子。
HD协议使用一个根创建层次化的子钥,孙钥,以及其他不可链接deterministically-generated整数值。每一子钥也从父钥获得deterministically-generated的种子,称为链码, 即使一个链码被入侵,允许主链码能继续使用, 比如当一个基于网络的公钥分发项目被黑掉,并不影响此系统。
point( (parent_private_key + lefthand_hash_output) % G ) == child_public_key point(child_private_key) == parent_public_key + point(lefthand_hash_output)
如前所述,用HD协议生成公钥的需要4个输入值:
- 父私钥和父公钥是常规压缩256位的 ECDSA非对称钥匙对。
- 父链码看上去像是随机256位数据。
- 索引值是一个由程序设定的32位整数。
在上图所示的范式,父链码,父公钥,索引值送入一个单向密码散列算法(HMAC-SHA512)中产生512位deterministically-generated-but-seemingly-random 数据。散列输出右侧256位作为新的子链码。散列输出的左侧256位作为整数值,输入父私钥或父公钥,分别创建一个子私钥和子公钥:
point( (parent_private_key + lefthand_hash_output) % G ) == child_public_key point(child_private_key) == parent_public_key + point(lefthand_hash_output)
指定不同的索引值将从同一父钥创建不同的不可链接子钥。重复的这个进程,将使子钥使用子链码创建不可链接孙钥。
因为创建子钥需要钥匙和链码,钥匙和链码一起被称为扩展钥匙。扩展私钥和其相应的扩展公钥相同的链码。(顶级父)主私钥和主链码来自随机数据,如下所示。
从128位,256位或512位的随机数据中创建出根种子。为了使用对应的设置来创建衍生的钥匙,只需要备份根种子的128位,以获得每个键由一个特定的钱包程序使用特定的设置。
(警告:在撰写本文时,基于HD协议的钱包程序并不会完全兼容,所以为了获得一个指定的基本种子,用户必须使用相同的钱包软件及其对应的设置。)
对根种子散列后创建seemingly-random的512位数据, 从根种子起创建出主私钥和主链码(即, 主扩展私钥)。使用函数point()从主私钥创建主公钥,与主链代码,即主扩展公钥。主扩展键在功能上相当于其他扩展键;不同的是他在层次化结构的顶部位置。
Hardened Keys 加固钥匙
加固扩展键解决正常扩展键所引起潜在问题。如果攻击者获得一个正常的父链码和父公钥,他能通过强力攻击找到所有的派生出来的链码。如果攻击者也得到一个子私钥,孙私钥,以及派生的私钥,他可以使用链码和对应的私钥生成的所有扩展出来私钥。
也许更糟糕的是,攻击者可以反向推导出正常的孩子私钥,从一个子私钥中减去一个父链码来恢复父公钥,此种情况见见上面的插图所体现的儿童和父母。这意味着一个获得了扩展公钥和衍生的私钥的是可以恢复的公共的私钥,以及所有衍生出来的钥匙。
基于这个原因,扩展链码的公钥应该比标准的公钥更能安全。users should be advised against exporting even non-extended private keys to possibly-untrustworthy environments。(彻底不会翻译)
经过一些权衡, 加固钥匙的方式修复了常用方式。
在上面部分中描述的,常用的推导公式结合索引值、父代码,和父公钥来创建子链码代码和整型值, 整数与父私钥结合来生成子私钥。