大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
钱包的本质
私钥就是资金的所有和使用权。钱包控制对以太币的访问、管理私钥和地址、跟踪账户的余额。
私钥通过 ECDSA(椭圆曲线签名算法)推导出公钥,继而经过 Keccak 单向散列函数推导出地址。
具体分为以下三个步骤:
- 创建随机私钥 (64 位 16 进制字符 / 256 比特 / 32 字节)
- 从私钥推导出公钥 (128 位 16 进制字符 / 512 比特 / 64 字节)
- 从公钥推导出地址 (40 位 16 进制字符 / 160 比特 / 20 字节)
以太坊黄皮书上关于钱包(私钥、公钥、地址)的描述如下:
椭圆曲线
公钥密码学
加密通常分为对称加密与非对称加密。在对称密码中,由于加密和解密的密钥相同,所以必须向接收者配送密钥用于解密。但发送密钥过程中,窃听者可以窃取密钥解密,不发送密钥吧,接收者无法解密,密钥必须发送,但又不能发送,这问题称为密钥配送问题。在非对称加密中,将密钥分为加密密钥和解密密钥,也就是我们常说的公钥和私钥。
一个公钥密码学系统必须满足如下要求:
- 根据一个给定的公钥从而推导出它对应的私钥在计算上是不可行的。
- 可以证明一个人知道与该公钥相对应的私钥,而不透露关于私钥的任何有用信息。
椭圆曲线原理
椭圆曲线由满足下列形式方程的所有点组成:
y 2 = x 3 + a x + b y^2=x^3+ax+b y2=x3+ax+b
其中 4 a 3 + 27 b 2 ≠ 0 4a^3+27b^2\neq0 4a3+27b2=0(避免奇点)。
点加法
椭圆曲线上的点加法按照如下方式定义。
如果想要得到 点 P P P 和 点 Q Q Q 相加的结果:
首先,找到经过这两点的直线:
这条直线与椭圆曲线相交于第三个点:
最后,将这个交点关于 x x x轴 做对称:
其中,点 R R R 为 点 P P P 和 点 Q Q Q 相加的结果,即 P + Q = R P+Q=R P+Q=R 。
如果选取一个基准点 P P P ,使得它不断地加上自身,我们可以得到 1 ⋅ P , 2 ⋅ P , … , k ⋅ P 1\cdot P,2\cdot P,\ldots,k \cdot P 1⋅P,2⋅P,…,k⋅P 。点 P P P与自身相加的结果可以定义为过点 P P P 的切线与椭圆曲线相交得到交点,交点再关于 x x x轴 对称。
对于以太坊钱包中用到的椭圆曲线 s e c p 256 k 1 secp256k1 secp256k1 选取的基准点的 x x x轴 和 y y y轴 坐标分别为:
x = 55066263022277343669578718895168534326250603453777594175500187360389116729240 x=55066263022277343669578718895168534326250603453777594175500187360389116729240 x=55066263022277343669578718895168534326250603453777594175500187360389116729240
y = 32670510020758816978083085130507043184471273380659243275938904335757337482424 y=32670510020758816978083085130507043184471273380659243275938904335757337482424 y=32670510020758816978083085130507043184471273380659243275938904335757337482424
快速点加法
如果 x x x 是一个随机的 256 256 256 位的整数,那么需要多少步才能算出 x ⋅ P x\cdot P x⋅P 呢?我们可以知道, x ⋅ P x\cdot P x⋅P 的结果可以在 510 510 510 次点加法之内被计算出来。具体分析流程如下。
首先,计算出以下序列:
2 0 ⋅ P , 2 1 ⋅ P , 2 2 ⋅ P , 2 3 ⋅ P , 2 4 ⋅ P , … , 2 255 ⋅ P 2^0\cdot P, 2^1\cdot P, 2^2\cdot P, 2^3\cdot P, 2^4\cdot P, \ldots, 2^{255}\cdot P 20⋅P,21⋅P,22⋅P,23⋅P,24⋅P,…,2255⋅P
全部序列的计算需要 255 255 255次点加法。注意到, 2 n ⋅ P + 2 n ⋅ P = 2 n + 1 ⋅ P 2^n\cdot P+2^n\cdot P=2^{n+1}\cdot P 2n⋅P+2n⋅P=2n+1⋅P 。于是任意的 x x x 都可以由上述的序列所表示。如 1 000 … 000 ⏟ 254 个 0 1 1\underbrace{000\ldots 000}_{254个0}1 1254个0
000…0001 可以由 2 255 ⋅ P + 2 0 ⋅ P 2^{255}\cdot P+ 2^0\cdot P 2255⋅P+20⋅P 表示。因此,通过计算出相应的序列,再由这些序列表示对应的数值最多需要 510 510 510 次计算。
公钥和私钥
根据椭圆曲线点加法的定义,我们可以知道如果 X = x ⋅ P X=x\cdot P X=x⋅P ,在给定 X X X 的情况下我们无法计算出对应的 x x x(每进行一次点加法,点的位置都变化很大,因此我们无法预测出在给定初始点的情况下,需要经过多少次加法才能得到最终的点),但是在给定 x x x 的情况下,我们却可以快速地计算出 X X X 。因此,椭圆曲线密码学就利用了这一原理,在给定基准点 P P P 的情况下,随机生成私钥 x x x ,再利用椭圆曲线快速计算出公钥 X X X 。
由于上述模型计算出的点无法表示 512 512 512 位的标准公钥(因为上面计算的结果很有可能是浮点数, 512 512 512 位通常不够表示相应的结果),因此我们需要定义我们的椭圆曲线在一个有限域上,也就是说曲线上所有点的坐标取值只能为整数。因此,我们将椭圆曲线的定义转化为:
y 2 ≡ x 3 + a x + b ( m o d p ) y^2\equiv x^3+ax+b\qquad (mod\quad p) y2≡x3+ax+b(modp)
其中 p p p 为 小于 2 256 2^{256} 2256 的最大质数。因此,修改后的椭圆曲线可能如下所示:
注意到,其中关于x轴对称的部分没有画出(这个部分不是很明白)。
应用
如何应用椭圆曲线?需要考虑两个问题:
- 如何证明你知道私钥 x x x 。
- 在证明过程中会不暴露关于私钥 x x x 的相关信息。
问题一
对于第一个问题,我们可以根据 X = x ⋅ P X=x\cdot P X=x⋅P 计算出 X X X 如果我们知道 x x x 的话。根据椭圆曲线点加法的定义,我们可以知道:
x ⋅ P + r ⋅ P = ( x + r ) ⋅ P x\cdot P+r\cdot P=(x+r)\cdot P x⋅P+r⋅P=(x+r)⋅P
我们将上式进行修改可以得到:
h a s h ( m , r ⋅ P ) ⋅ x ⋅ P + r ⋅ P = ( h a s h ( m , r ⋅ P ) × x + r ) ⋅ P hash(m,r\cdot P)\cdot x\cdot P+r\cdot P=(hash(m,r\cdot P)\times x+r)\cdot P hash(m,r⋅P)⋅x⋅P+r⋅P=(hash(m,r⋅P)×x+r)⋅P
由于 X = x ⋅ P X=x\cdot P X=x⋅P :
h a s h ( m , r ⋅ P ) ⋅ X + r ⋅ P = ( h a s h ( m , r ⋅ P ) × x + r ) ⋅ P hash(m,r\cdot P)\cdot X+r\cdot P=(hash(m,r\cdot P)\times x+r)\cdot P hash(m,r⋅P)⋅X+r⋅P=(hash(m,r⋅P)×x+r)⋅P
令 R = r ⋅ P , s = h a s h ( m , R ) × x + r R=r\cdot P, s=hash(m,R)\times x+r R=r⋅P,s=hash(m,R)×x+r ,可以得到:
h a s h ( m , R ) ⋅ X + R = s ⋅ P (1) hash(m,R)\cdot X+R=s\cdot P \tag{1} hash(m,R)⋅X+R=s⋅P(1)
如果我们知道 x x x ,我们可以选定一个 m , r m, r m,r ,从而根据 R = r ⋅ P , s = h a s h ( m , R ) × x + r R=r\cdot P, s=hash(m,R)\times x+r R=r⋅P,s=hash(m,R)×x+r 计算出 R R R 和 s s s 使得(1)成立。于是,我们可以得出结论,如果能够给出 m , R , s m, R, s m,R,s 使得(1)成立,那么便能够证明其知道私钥 x x x 。
如果我们不知道 x x x ,想要使得(1)成立。固定任意两个数,去寻找第三个数,这个过程根据圆锥曲线点加法的计算方式,都是计算上不可行的。
因此,我们可以得出结论:
- 如果知道 x x x ,那么应该能够提供 m , R , s m, R, s m,R,s 的工作值。
- 如果不知道 x x x ,那么应该不能提供 m , R , s m, R, s m,R,s 的工作值。
问题二
首先, m , R m, R m,R 的值与 x x x 无关,因此它不能揭示任何与私钥 x x x 相关的信息。
我们知道 s = h a s h ( m , R ) × x + r s=hash(m,R)\times x+r s=hash(m,R)×x+r ,因此我们可以得到:
x = s − r h a s h ( m , R ) x=\frac{s-r}{hash(m,R)} x=hash(m,R)s−r
因此,要想知道私钥 x x x 的值,我们需要首先获取 r r r 的值,但是根据 R = r ⋅ P R=r\cdot P R=r⋅P ,由椭圆曲线点加法的计算方式,我们无法从 R R R 的取值来推导 r r r 的取值(计算上不可行)。
所以,在证明过程中不会暴露关于私钥 x x x 的信息。
数字签名
通过验证等式 h a s h ( m , R ) ⋅ X + R = s ⋅ P hash(m,R)\cdot X+R=s\cdot P hash(m,R)⋅X+R=s⋅P 是否成立, m , R , s m, R, s m,R,s 可以被用来证明某人知道公钥 X X X 对应的私钥 x x x ,其中 X = x ⋅ P X=x\cdot P X=x⋅P 。
如果 m m m 是一个给定的具体信息(用户即将发送),那么用户只需要提供 R R R 和 s s s 作为其数字签名,最终接收方验证等式 (1)是否成立来验证这个信息是否是由相应的用户所发生的(即用户对这条消息进行了签名认证)。如果验证通过,说明是由本人发送的这条消息,并不是其他人伪造的,在区块链上,这条消息就是一个交易请求信息,如果矿工验证通过了这个消息,那么他将会执行这条消息中的内容。
也就是说,对于一个(发送者发出的)具体消息 m m m ,通过提供数字签名 R R R 和 s s s 一个人可以证明他知道(发送者)公钥 X X X 对应的私钥 x x x (消息是由本人发出的)。
以太坊私钥存储(Keystore)文件
通常一些钱包会对用户的私钥进行加密等操作,使得私钥不会被直接暴露在外界,这提高了私钥的安全性。
以太坊客户端提供了私钥管理的功能,通过下述命令:
$ geth account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {
008aeeda4d805471df9b2a5b0f38a0c3bcba786b}
$ geth account list
Account \#0: {
8a1c4d573cc29a96547816522cfe0b266e88abac} keystore:~/.ethereum/keystore/UTC--<created_date_time>-- 008aeeda4d805471df9b2a5b0f38a0c3bcba786b
上述命令将会创建一个新的账户,账户信息以 keystore 文件的形式存放在 ~/.ethereum/keystore 目录( Linux 操作系统)下。如果你丢失了这个文件,你就丢失了私钥,意味着你失去了签署交易的能力,意味着你的资金被永久的锁定在了你的账户里。
当然,你可以直接把你的以太坊私钥存储在一个加密文件里,但是这样你的私钥容易受到攻击,攻击者简单的读取你的文件、用你的私钥签署交易,把钱转到他们的账户中。你的币会在你意识到发生什么了之前的短时间内丢失。
这就是以太坊 keystore 文件被创建的原因:它允许你以加密的方式存储密钥。这是安全性(一个攻击者需要 keystore 文件和你的密码才能盗取你的资金)和可用性(你只需要keystore文件和密码就能用你的钱了)两者之间完美的权衡。
为了让你发送一些以太币,大多数的以太坊客户端会让你输入密码(与创建账户时密码相同)以解密你的以太坊私钥。一旦解密,客户端程序就得到私钥签署交易,允许你移动资金。
Keystore 文件格式
大致内容如下:
{
"crypto" : {
"cipher" : "aes-128-ctr",
"cipherparams" : {
"iv" : "83dbcc02d8ccb40e466191a123791e0e"
},
"ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
"kdf" : "scrypt",
"kdfparams" : {
"dklen" : 32,
"n" : 262144,
"r" : 1,
"p" : 8,
"salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
},
"mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
},
"id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
"version" : 3
}
其中包括
- cipher:对称 AES 算法的名称;
- cipherparams:上述 cipher 算法需要的参数;
- ciphertext:你的以太坊私钥使用上述 cipher 算法进行加密后的密文;
- kdf:密钥生成函数,用于让你用密码加密 keystore 文件;
- kdfparams:上述 kdf 算法需要的参数;
- Mac:用于验证密码的代码(校验项);
工作流程
1. 加密你的私钥
为了确保你的私钥没有在文件中明文存储(即任何人只要能得到这个文件就能读),使用强对称算法(cipher)对其加密至关重要。
这些对称算法使用密钥来加密数据。加密后的数据可以使用相同的方法和同样的密钥来解密,因此算法命名为对称算法。在本文中,我们称这个对称密钥为解密密钥,因为它将用于对我们的以太坊私钥进行解密。
以下是 cipher,cipherparams 和 ciphertext 对应的概念:
- cipher 是用于加密以太坊私钥的对称加密算法。此处 cipher 用的是 aes-128-ctr 加密模式。
- cipherparams 是 aes-128-ctr 加密算法需要的参数。在这里,用到的唯一的参数 iv ,是 aes-128-ctr 加密算法需要的初始化向量。
- ciphertext 密文是 aes-128-ctr 函数的解密的输入。
所以,在这里,你已经有了进行解密以太坊私钥计算所需要的一切。等等。你需要首先取回你的解密密钥。
2. 用你的密码来保护它
要确保解锁你的账户很容易,你不需要记住你的每一个又长又非用户友好型的用于解密 ciphertext 密文解密密钥。相反,以太坊开发者选择了基于密码的保护,也就是说你只需要输入密码就能拿回解密密钥。
为了能做到这一点,以太坊用了一个密钥生成函数,输入密码和一系列参数就能计算解密密钥。
这就是 kdf 和 kdfparams 的用途:
- kdf 是一个密钥生成函数,根据你的密码计算(或者取回)解密密钥。在这里,kdf 用的是 scrypt 算法。
- kdfparams 是 scrypt 函数需要的参数。在这里,简单来说,dklen、n、r、p 和 salt 是 kdf 函数的参数。
在这里,用 kdfparams 参数对 scrypt 函数进行调整,反馈到我们的密码中,你就会得到解密密钥也就是密钥生成函数的输出。
3. 确保你的密码是对的
我们描述了用密码和 keystore 文件生成以太坊私钥所需要的所有东西。然而,如果解锁账户的密码错误会发生什么?
根据迄今为止我们所看到的,所有操作(密码派生和解密)都会成功,但是最终计算的以太坊私钥不是正确的,这首先违背了密钥文件的使用初衷!
我们要保证输入解锁账户的密码是正确的,和最初创建 keystore 文件时一样(回想一下 geth 下创建新账户时两次输入的密码)。
这就是 keystore 文件中 mac 值起作用的地方。在密钥生成函数执行之后,它的输出(解密密钥)和 ciphertext 密文就被处理,并且和 mac(就像一种认可的印章)作比较。如果结果和 mac 相同,那么密码就是正确的,并且解密就可以开始了。处理过程如下图所示:
总体流程
系统总体工作流程如下:
- 输入密码。
- 这个密码与 kdfparams 作为 kdf 密钥生成函数的输入,来计算解密密钥。
- 解密密钥 与 chipertext 进行连接并进行处理,和 mac 进行比较来确保密码的正确性。
- 通过 chiper 对称函数(参数为 chiperparams )用解密密钥对 chipertext 密文解密,解密结果为账户的私钥。
钱包技术概述
第一类是非确定性钱包,其中保存的每一个私钥都是通过不同的随机数相互独立地生成的。私钥之间没有任何关联。这类钱包被称为 JBOK (Just a Bunch Of Keys)钱包。
第二类钱包是确定性钱包,其中所有的密钥都是从一个主密钥衍生而来的,这个主密钥就是种子密钥。这类钱包中所有的密钥之间都存在关联关系,如果获得了“种子密钥”,则可以重新生成所有密钥。确定性钱包有多种密钥派生方法。最常用的派生方法是使用一个类似树形的结构,我们称之为层级式确定性(hierarchical deterministic)钱包,或简称为 HD 钱包。
注:用户如果长期使用同一个账户地址进行交易,那么用户的账户就可能存在被跟踪的风险,导致用户的隐私泄露。因此,为了保证隐私性,通常需要用户不断更换发起交易的地址。在比特币网络中,由于账户模型是 UTXO 模型,每笔交易可以设置一个找零账户,交易中剩余的金额会被转入这个账户,这就使得同一个用户可以使用不同的地址发起交易(交易结束后将剩余的钱转入一个新的地址,这个在以太坊上不适用,因为以太坊需要收取交易的手续费)。这就使得一个用户可能同时拥有多个比特币的地址,因此要管理多个这样的地址,钱包中也就需要管理多个私钥。由于非确定性钱包中,各个私钥之间不相关,因此管理起来相对麻烦(需要同时管理多个私钥),而确定性钱包各个私钥之间存在关联,可以通过主密钥来派生出所有的子密钥,因此钱包中只需要管理主密钥,就可以衍生出所有相关的密钥。
非确定性钱包
上述以太坊通过存储 keystore 文件来管理密钥,就是一种确定性钱包。
确定性钱包
主要以层级式确定性钱包为例。它的目的是让人们更容易地从单一的“种子”中衍生出多个密钥。目前,确定性钱包最高级的形式便是由比特币 BIP-32 标准定义的 HD 钱包。HD钱包可以保存用树状结构推导的多个密钥,比如一把私钥可以推导出一系列子密钥,每一个子密钥都可以推演出一系列孙子密钥,如此类推至于无穷。如图所示:
其中 BIP-39 中提出了利用助记词生成种子密钥的标准,这使得人们不用去与复杂的密钥(二进制串)打交道,而是使用相对易于理解的助记词。
以下是一些钱包设计中的行业标准 BIP (Bitcoin Improvement Proposal):
-
基于 BIP-32 的层级式确定性钱包的
-
基于 BIP-39 的助记词标准
-
基于 BIP-43 的多用途层级式确定性钱包
-
基于 BIP-44 的多币种和多账户钱包
助记词标准 (BIP-39)
助记词中的单词代表用来生成钱包的种子密钥中的内容。这一串助记词足够用来重新创建种子密钥,进而恢复整个钱包中所有从这个种子派生而来的密钥。本节说明了如何生成助记词以及如何通过助记词创建种子密钥。
生成助记词
助记词由钱包根据BIP—39所定义的标准流程自动生成,钱包从随机源获取一个随机数,然后添加校验码,再把这个数字映射为一串英文单词:
- 创建一个 128 128 128 比特或 256 256 256 比特的密码学强度的随机数,我们姑且称之为 S S S 。
- 取出 S S S 的 S H A − 256 SHA-256 SHA−256 哈希值的前( S 的 长 度 / 32 S的长度/32 S的长度/32)比特,作为随机数 S S S 的校验值
- 将上一步得到的校验值加到随机数 S S S 的末尾。
- 以 11 11 11 比特为单位,将随机数 S S S 与校验值的结合数分成多个组。
- 将每一个 11 11 11 比特的值都根据预先定义的字典映射为单词(这个字典包含 2048 2048 2048 个简单的英文单词,正好覆盖所有 11 11 11 比特的可能范围)。
- 保持初始的次序,得出的单词字符串即我们所需的助记词。
从助记词到种子密钥
助记词代表128比特或256比特的随机数。使用密钥扩展算法,例如 PBKDF2,可以将这个随机数衍生成 512 512 512 比特长的种子,进而用来构建确定性钱包和派生其他密钥。
密钥扩展算法需要两个参数:助记词和盐(salt) 。加盐的目的是防止通过循环表格的方式来实现暴力激活成功教程。在 BIP-39 标准中,加盐还有另外一个目的:引入额外的密码来保护种子密钥。
- PBKDFZ 密钥扩展算法的第一个参数是步骤 6 中产生的助记词。
- PBKDFZ 密钥扩展算法的第二个参数是“盐”,盐的内容由川户提出,可以是一个可选的密码,并跟 ”mnemonic” 组合在一起。
- PBKDFZ 针对助记词和盐进行 2048 2048 2048 轮哈希运算,使用的是 H M A C − S H A 512 HMAC-SHA512 HMAC−SHA512 算法,产生一个 512 512 512 比特的数作为最终输出。这个数字就是种户密钥。
BIP-39 标准允许用户在生成种子密钥的过程中使用可选密码。如果用户没有设定密码,那么默认使用字符串 ”mnemonic” 进行助记词的密钥扩展运算,生成一个特定的 512 512 512 比特的种子密钥。如果用户提供了密码,那么对于同样的助记词,密钥扩展运算会生成完全不同的种子密钥。实际上,给定一组助记词,每一个密码都会导致不同的种子密钥。特别是,这里没有正确或者错误的密码,所有密码都可以生成用来衍生无数钱包地址的种子密钥。可能的钱包的范围非常巨大,如果密码的复杂度足够强,那么暴力激活成功教程或猜测都没有可能实现。举个栗子?:
层级式确定性钱包 (BIP-32)
BIP-32 为 HD 钱包的核心提案,说明了私钥生成方法以及树状结构的构造方式。其定义了如下两个内容:
- 根据父节点公(私)钥匙派生子节点公(私)钥的算法。
- 将派生出来的钥匙对组织成树状结构的方法。
在 BIP-32 中根据父节点去派生子节点的方法被称作 Child Key Derivation Function,简称为 CKD。CKD 根据如下 3 个参数去生成子节点:
-
父节点私钥或者公钥 (Parent Private/Public Key)
-
父节点链码 (Parent Chain Code)
-
子节点序号 (Child Index)
如何生成子公钥?
根据父节点公钥生成子节点公钥的流程如下图:
- 把父节点公钥、父节点链码、子节点序号作为参数求 HMAC-SHA512 得到 512 512 512 位输出;
- 把步骤 1 的输出拆分为两个等长的 256 256 256 位串,分别标记为 L L L、 R R R;
- 把步骤 2 的输出 L L L 和父节点公钥做运算得到子节点公钥 (Child Public Key);
- 把步骤 2 的输出 R R R 当做子节点链码 (Child Chain Code);
如何生成子私钥?
根据父节点私钥 (Parent Private Key) 生成子节点私钥 (Child Private Key) 的流程如下图:
- 根据父节点私钥和椭圆曲线乘法推导出父节点公钥 (Parent Public Key);
- 把父节点公钥、父节点链码、子节点序号作为参数求 HMAC-SHA512 得到 512 512 512 位输出;
- 把步骤 2 的输出拆分为两个等长的 256 256 256 位串,分别标记为 L L L、 R R R;
- 把步骤 3 的输出 L L L 和父节点私钥做运算得到子节点私钥 (Child Private Key);
- 把步骤 3 的输出 R R R 当做子节点链码 (Child Chain Code);
为什么要有 Chain Code?
钱包安全的核心在私钥,而公钥则比较容易被找到,如果子节点生成过程只依赖父节点公钥和子节点序号,那么黑客拿到父节点公钥之后就能复原出所有子节点、孙节点的公钥,这样就会破坏隐私性,CKD 里面引入的 Chain Code 则是在整个子节点派生过程中引入确定的随机数,为 HD 钱包的隐私性增加了一重保障。
注:公钥是可以被暴露在外的,Chain Code 如果在钱包中被安全的保存,因此光利用公钥是无法还原整棵树的公钥的。但是,如果 Chain Code 也被暴露,那么黑客同样可以还原所有子节点、孙节点的公钥。
什么是 Extended Key?
因为在子节点生成过程中会同时用到父节点公钥和父节点链码,BIP-32 里面约定把两者拼接再做特定结构编码产生的结果叫做 Extended Key,也叫做可扩展的钥匙,顾名思义就是根据 Extended Key 我们就可以开始派生子节点。父节点公钥、私钥和链码结合产生的 Extended Key 分别是:
Extended Private Key
=Private Key
+Chain Code
, 标记为 xpriv,可用于派生子节点私钥和公钥Extended Public Key
=Public Key
+Chain Code
, 标记为 xpub,只能用于派生出子节点公钥
因为从 Extended Key 可以解出父节点私钥、公钥和链码,可以说 Extended Key 代表了 HD 钱包中某个分支、子树的根或者起点,也正是因为这种特性,对 Extended Key 的数据保密要格外小心。
什么是 Master Key?
既然是一颗树,那么必须要有一个根节点(主节点),所有子节点的生成都从这个根节点开始。这里,主节点由种子密钥生成。
通过将种子密钥拆分成两个 256 256 256 位的数字,分别作为主节点私钥和主节点链码,主节点公钥可以由主节点私钥计算得出,而后递归生成子节点。
安全增强的 CKD 函数
因为区块链钱包里面保存的私钥能转移用户的资产,对安全性再怎么强调都不为过,对于上面的子节点私钥和公钥生成函数是否足够安全呢?我们设想下面的场景:
- 如果黑客知道了父节点的公钥和链码,那么他可以生成所有子节点、孙节点的公钥、链码、地址,这样会严重破坏 HD 钱包的隐私性
- 如果黑客在上面的基础上知道了某个孙节点的私钥,那么所有重孙节点的私钥都能被推导出来,父节点的私钥也可能被推导出来,这样整个 HD 钱包就沦陷了
如果安全问题是没有办法彻底避免的,如何在某个子节点私钥泄露的时候把破坏性降到最低呢?这就需要对 CKD 函数稍作改进,在 BIP-32 中称之为 安全增强的子私钥派生函数,记为 HCKD (Hardened Child Key Derivation),原来的 CKD 函数 (Normal Child Key Derivation) 和安全增强的 CKD 函数流程对比如下图:
安全增强的 CKD 函数产生的节点属性称呼也响应的发生变化:
- 增强的子节点私钥:Hardened Child Private Key
- 增强的子节点公钥:Hardened Child Public Key(只能根据增强的子节点私钥推导而来,必须先生成子节点私钥,再推到子节点公钥)
不同点在于,安全增强的 CKD 函数中子节点私钥的生成不再使用父节点公钥,而是直接使用父节点私钥,因为相比私钥而言公钥更容易被黑客截获,这样必须在有父节点私钥的情况下才能推导出子节点私钥,只靠父节点的公钥和链码不能推导出增强的子节点公钥。这样子节点之间的兄弟关系就不那么容易被获悉,而即使某个增强子节点私钥泄露,也不会影响到父节点。
BIP-32 约定 CKD 函数的节点序号取值范围在 [ 0 , 2 31 ) [0,2^{31}) [0,231) 之间,而 HCKD 的节点序号在 [ 2 31 , 2 32 ) [2^{31},2^{32}) [231,232) 之间,这样每个节点就可以生成 2 31 2^{31} 231 个子节点。
注:在 CKD 的情况下,如果知道了父节点的公钥和链码,可以推到出所有子节点、孙节点的公钥和链码;如果再知道了某个孙节点的私钥,就可以推到重孙节点的私钥;同时由于孙节点的私钥是由其父节点的私钥以及其父节点的公钥经过单向哈希运算的结果共同作用得到的,因此有可能推到出其父节点的私钥。 HCKD 的情况下,即使黑客知道了父节点的公钥和链码,也无法推到子节点的公钥和链码,因为这一步需要用到父节点的私钥。某个孙节点的私钥泄露之后,由于不知道孙节点的链码,因此也无法推导其他的重孙节点。
层级式确定性钱包的标识符(路径)
HD 钱包中的密钥采用 “路径” 的命名规范进行表示,根据树形结构在每一层之间采用 “/” 分割。从主私钥派生出的私钥由 m 开头,而从主公钥派生出的公钥由 M 开头。因此,主私钥派生的第一个子私钥表示为 m / 0 m/0 m/0,主公钥派生出的第一个子公钥表示为 M / 0 M/0 M/0 。第一个子私钥的第二个下一级孙子私钥表示为 m / 0 / 1 m/0/1 m/0/1,以此类推。
层级式确定性钱包的树状结构
显然,BIP-32 的在钱包安全性、易用性方面做了比较不错的平衡,但是不同的钱包应用开发者可以自定义自己的节点结构,这就很容易导致没有办法 100% 保证在使用了 HD 钱包 A 的用户能将自己的种子导入到 HD 钱包 B 中还能正常工作;也没有办法保证 HD 钱包能支持多个链的私钥管理。
因为这个原因,比特币社区在 BIP32 的基础上提出了比较规范的 BIP-43 和比较具体的 BIP-44,两者的目的在于就 HD 钱包子节点派生路径的模式、每段的含义上做出具体的规定,形成共识,事实上现如今的 HD 钱包都遵循了 BIP-32 和 BIP-44 的规定,也只有遵循了这两个规范的钱包应用才是大概率完全兼容的。
BIP-44 的内容相比 BIP-32 就简单很多,里面规定了子节点派生路径的范式:
m / purpose' / coin_type' / account' / chain / address_index
示例如下:
m/44'/60'/0'/0/0
每个段的含义分别是:
- CKD:
m
: 使用CKDpriv
,M
则表示使用CKDPub
- Purpose:
44'
,hardened
, 遵循哪个规范, 44 意味着 BIP-44 - Coin:
60'
,hardened
, 60 指代以太坊 - Account:
0'
,hardened
, 账户编号(用户自定义划分用途) - Chain:
0
, 对于非比特币路径都是 0(0表示接受地址,1表示找零地址,找零地址只存在于比特币种) - Index:
0
, 具体的账户节点
以太坊元交易
通过元交易,我们可以用一个完全没有以太币的账户来与区块链进行交互。对于推动以太坊的普及来说,这种技术可能是不可或缺的。用户并不关心去中心化或者私钥;他们更关心的是可以使用你的 Dapp 来做一些对他们而言重要的事情。
参考
[1] 关于钱包的密码学基础
[2] 如何通过私钥创建以太坊钱包地址?
[3] 什么是以太坊私钥储存(Keystore)文件?
[4] Mastering Ethereum
[5] 以太坊元交易
[6] What is the math behind elliptic curve cryptography?
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/178812.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...