主页 > imtoken钱包官方苹果 > HD钱包学习总结

HD钱包学习总结

imtoken钱包官方苹果 2023-01-17 04:34:59

钱包用于比特币/以太坊等公共链。 钱包主要用于管理用户的私钥和用户在链上的数字货币,即用私钥对交易进行签名。 私钥可用于为特定消息生成签名,可以使用公钥验证签名而无需泄露私钥。

因为私钥极其重要,一旦泄露,就意味着数字资产的所有权掌握在他人手中。 理论上,私钥可以是任意一串随机数,记忆难度大,没有规律可循。 需要用一些密码学的方法来管理密钥对(一个密钥对包括一个私钥和一个对应的公钥),既方便管理又足够安全。

1. 非确定性钱包

如果只是随机生成一个数字串作为私钥,可以使用密码安全伪随机数生成器(CSPRNG,Cryptographically secure pseudorandom number generator,密码学安全伪随机数生成器)。 这些私钥是完全独立的,对应的公钥也不相关。 管理这种密钥对的钱包称为非确定性钱包。 早期的比特币地址是非确定性钱包。 非确定性钱包最大的麻烦是在导入和导出密钥对时,必须将钱包中的所有密钥对一一操作。

2. 确定性钱包

为了方便应用,提出了一种密钥对生成方法来解决非确定性钱包的这些问题:密钥对来源于原始种子主密钥。 最常见的推导方法是层次确定性(hierarchical deterministic比特币私钥格式转换器,简称HD)。 通过这种方式生成的钱包密钥对也称为确定性钱包。

n 通过一个共同的种子可以推导出多个私钥,种子推导私钥采用不可逆哈希算法。 当您需要备份钱包私钥时,只需要备份种子(大多数情况下,为了复制方便,种子由12个助记词生成),钱包可以导入所有私钥只需导入助记词即可。 HD钱包可以在不知道私钥的情况下生成大量公钥。 这个特性非常适合只负责收款的服务。

我们从HD钱包的代际关系来介绍一下:

熵(128位)→助记词(12个)→种子(512位)→私钥→公钥→地址。

2.1 助记符和熵

顾名思义,助记词就是为了方便记录一长串不规则的数字而映射出来方便复制记忆的助记词。 因为助记词词典一共有2048个助记词,一个11位长度的索引(2^11=2048)就可以定位到所有的助记词。 记住这n个助记词和它们的顺序,那么它们的索引值就可以组合成(n11)位长*的数字串。

反之,将(n*11)位数的数字串切成n份,每份长度为11位,作为助记词的索引,根据索引从助记词库中获取助记词并永久记录.

下面是生成这个n*11的数字串并转换成助记词的过程:

比特币钱包导出私钥_比特币私钥格式转换器_比特币 私钥恢复钱包

图片.png

生成长度为128/160/192/224/256位(bits)的随机序列号串,称为熵; 对熵进行hash,取hash后数据串的前4/5/6/7/8位作为校验和(长度=熵长度/32); 结合熵和校验和,即总长度为132/165/198/231/264位; 将上述结果每11位切割一次,得到12/15/18/21/24个助记词索引; 根据助记词索引对助记词词典中的词进行匹配,得到一组完整的助记词;

下图以长度为128位的熵为例展示了上述过程:

[图片上传失败...(image-4d4e04-1532831639402)]

2.2 种子

可以从助记符中导出长度为 128 到 256 位的熵。 熵可以通过 PBKDF2 函数导出到更长的(512 位)种子。

注:PBKDF2(Password-Based Key Derivation Function 2)是常用的密钥拉伸算法之一。 在密码学中,Key stretching 技术被用来增强弱密钥的安全性,增加了 Brute-force 攻击尝试破解每个可能密钥的时间,增加了攻击难度。 其基本原理是使用一个随机函数(如HMAC函数),将明文和salt值作为输入参数,然后重复上述操作,最终生成密钥。 如下所示:

比特币私钥格式转换器_比特币钱包导出私钥_比特币 私钥恢复钱包

图片.png

PBKDF2函数的实现如下:

DK = PBKDF2(PRF, Password, Salt, c, dkLen)
实现:
DK = T1 || T2 || ... || Tdklen/hlen
Ti = F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
U1 = PRF(Password, Salt || INT_32_BE(i))
U2 = PRF(Password, U1)
...
Uc = PRF(Password, Uc-1)

比特币私钥格式转换器_比特币 私钥恢复钱包_比特币钱包导出私钥

这个函数有几个输入参数:

2.3 主私钥和主链码

主密钥和主链码可以从根种子生成。 计算方法非常简单。 根种子由 HMAC-SHA512 函数计算一次。 左边256位为主私钥,右边256位为主链码。 通过椭圆曲线算法将主私钥推送到主公钥。 私钥构成主密钥对。 主链码作为导出从密钥的熵。

比特币私钥格式转换器_比特币 私钥恢复钱包_比特币钱包导出私钥

图片.png

从私钥生成公钥的算法可以在下面的椭圆曲线算法部分找到。 这里主公钥的长度是264位,因为格式是压缩格式公钥(前缀(8位)+x轴方向坐标(256位)),见下一节介绍。

2.4 子键

子密钥可以从父密钥派生,CKD 函数对以下三个输入执行单向散列。

计算方法见下文以太坊HD钱包中的介绍。

比特币 私钥恢复钱包_比特币私钥格式转换器_比特币钱包导出私钥

图片.png

索引号个数为2^32,每个父键可以导出一半个子键(索引号从0x00到0x7fffffff(0~2^31-1)会生成普通键;索引号会生成增强键从 0x80000000 到 0xffffffff)。

推导采用不可逆HMAC-SHA512不可逆加密算法。 子键不能向上推导父键,同时也不能横向推导同级键。 生成的512位数据中左256位作为子私钥,右256位作为子链码。

2.5 扩展密钥

CKD派生子密钥的三要素中比特币私钥格式转换器,父密钥与链码的组合统称为扩展密钥。

包含私钥的扩展密钥用于推导出子私钥,从子私钥可以推导出对应的公钥和比特币地址; 包含公钥的扩展密钥用于导出子公钥;

当扩展密钥使用 Base58Check 进行编码时,它将使用特定的前缀进行编码:

与比特币的公钥和私钥相比,扩展密钥编码后得到的长度为512或513位。

2.6 子公钥

HD钱包的一个非常好用的特性就是在隐藏私钥的前提下,从公钥中推导出子公钥,大大增强了安全性。 在只需要生成地址接受比特币而无需消费的场景下非常有用。 公钥扩展密钥可以生成无穷无尽的公钥和比特币地址。 子公钥推导过程如下:

比特币私钥格式转换器_比特币 私钥恢复钱包_比特币钱包导出私钥

图片.png

此方法可用于创建非常机密的 public-key-only 公钥。 可用于接收比特币,但不能在此地址中花费任何比特币。 同时,在另一台更安全的服务器上,扩展私钥可以衍生出所有对应的可以签署交易和花钱的私钥。

注意:推导出来的子公钥和子私钥是一对秘钥。 在算法的实现中,子公钥的推导需要先计算子私钥,再计算子公钥。

2.7 增强扩展密钥

密钥需要严格保管,以免泄露。 泄露私钥意味着对应地址上的币可以被转走。 泄露公钥意味着HD钱包的隐私被泄露。 强化子密钥派生解决了以下两个问题:

虽然公钥泄露不会导致币丢失,但是包含该公钥的扩展密钥泄露会导致该根节点衍生出的所有扩展公钥泄露,一定程度上破坏了隐私. 如果扩展公钥(包含链码)和子私钥被泄露,它们可以用来推导出所有其他子私钥,因为子链码可以通过索引得到。 更糟糕的是,子私钥和父链码可以用来推断父私钥。

这里,BIP32协议将CKD函数改为HKD(硬化密钥派生公式),生成增强的密钥派生函数。 “打破”父公钥和子链代码之间的关系。 HKD 与一般的派生子私钥几乎相同,只是使用父私钥而不是父公钥作为输入。

CKD函数从扩展密钥序列号(0x00到0x7fffffff)、父链码和父公钥中导出子链码和子公钥,子私钥从父私钥中导出; 而 HKD 使用父私钥、父链码序列号和增强扩展密钥(0x80000000 到 0xffffffff)来派生增强器私钥和增强器链码。

比特币 私钥恢复钱包_比特币钱包导出私钥_比特币私钥格式转换器

图片.png

3.椭圆曲线算法

比特币钱包导出私钥_比特币 私钥恢复钱包_比特币私钥格式转换器

可以通过椭圆曲线算法从私钥计算出公钥,这是一个不可逆的过程。 公式如下:K = k * G。其中k是私钥,G是一个常数点,称为生成点,对所有比特币用户都是一样的,K是生成的公钥。 它的逆运算,被称为“求离散对数”——知道公钥K来求私钥k——是非常困难的,就像试图测试k的所有可能值一样,也就是蛮力搜索。

使用的secp256k1标准定义的特殊椭圆曲线如下:

比特币 私钥恢复钱包_比特币钱包导出私钥_比特币私钥格式转换器

图片.png

对应的公式为

y^2 mod p = (x^3 + 7) mod p
其中 p = 2^256 – 2^32 – 2^9– 2^8 – 2^7 – 2^6 – 2^4 – 1

上图中的x和y定义在实数范围内。 如果x和y都是整数,只有一些离散的坐标值符合secp256k1椭圆曲线。 例如,当p=17时,x和y的离散坐标图如下:

比特币私钥格式转换器_比特币 私钥恢复钱包_比特币钱包导出私钥

图片.png

定义椭圆曲线坐标的加法:给定椭圆曲线上的两个点 P1(x1,y1) 和 P2(x2,y2),椭圆曲线上必须有第三个点 P3(x3,y3) = P1 + P2。 在几何学中,第三点 P3 可以通过在 P1 和 P2 之间画一条线来确定。 这条线恰好与椭圆曲线上的一个点相交。 该点表示为 P3'=(x,y)。 然后在x轴上做映射得到P3=(x,-y)。 椭圆曲线坐标的乘法很好理解,可以分解成多次加法。

比特币钱包导出私钥_比特币 私钥恢复钱包_比特币私钥格式转换器

图片.png

4.公钥

公钥是椭圆曲线上的一个点,由一对坐标(x,y)组成。

公钥通常由前缀 04 后跟两个 256 位数字表示。 其中一个 256 位数字是公钥的 x 坐标,另一个 256 位数字是 y 坐标。 例如:

K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

为什么坐标地址前有前缀04? 因为前缀04用来表示非压缩格式公钥,有完整的x,y坐标,而压缩格式公钥以02或03开头。

4.1 压缩格式公钥

引入压缩格式公钥以减少比特币交易的字节数,从而节省运行区块链数据库的节点上的磁盘空间。 椭圆曲线上的一个点实际上是一个数学方程的解。 因此,如果我们知道公钥的 x 坐标,我们可以通过求解方程 y2 mod p = (x3 + 7) mod p 得到 y 坐标。 这种方案让我们只存储公钥的x坐标,省略y坐标,从而将公钥的大小和存储空间减少了256位,大大节省了大量的数据传输和存储。

为什么压缩格式的公钥有两个前缀02或03? 因为y的解来自平方根,所以有正有负。 y坐标可以是奇数也可以是偶数,分别对应正负。 因此,在生成压缩格式的公钥时,如果y为偶数,则使用02作为前缀; 如果 y 是奇数,则使用 03 作为前缀。 若将上述K以压缩格式表示:

K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

但是有一个问题:一个私钥可以生成两种不同格式的公钥——压缩的和未压缩的——从而生成两个不同的比特币地址。 较新的比特币客户端当前的默认格式是使用压缩公钥。

从钱包导出私钥时,有2种WIF格式(Wallet Import Format):

举例如下:

私钥hex:1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD
对应WIF格式:5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

比特币钱包导出私钥_比特币 私钥恢复钱包_比特币私钥格式转换器

私钥Hex-compressed: 1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD01 对应WIF-compressed:KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

base58编码见下节介绍。

5. 账户地址 5.1 比特币地址

比特币地址是一串以数字“1”开头的数字和字母。 例如:

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

通过公钥生成地址的算法如下:

ADDR = RIPEMD160(SHA256(PUBKEY))  //双hash

提高可读性,避免歧义,有效防止地址转录和打字错误。 比特币地址也用“Base58Check”编码。

ACCOUNT_ADDR = Base58Check(ADDR)

5.2 编码

Base64使用26个小写字母、26个大写字母、10个数字和2个符号(如“+”和“/”),通常用于对电子邮件中的附件进行编码。 Base58是Base64编码格式的一个子集,同样使用大小写字母和10个数字,但不包含0(数字0)、O(大写字母o)、l(小写字母L)、I(大写字母i) ), 以及两个字符“+”和“/”。 Base58 的字母表是:

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

Base58Check是比特币常用的一种Base58编码格式,在数据转录中加入了错误校验码来检查错误。 校验码长4个字节,加在待编码数据之后。 校验和是从待编码数据的哈希值中得到的,因此可以用来检测和避免输入中的错误。

在编码之前,我们首先需要给数据加上一个叫做“version byte”的前缀,用来明确需要编码的数据类型。 前缀如下:

比特币私钥格式转换器_比特币钱包导出私钥_比特币 私钥恢复钱包

图片.png

注意:

校验码的计算方法如下:

checksum = SHA256(SHA256(prefix+data))

比特币私钥格式转换器_比特币 私钥恢复钱包_比特币钱包导出私钥

取结果的前4个字节作为校验码。 这样就得到了前缀、数据和校验码三部分,然后在前面介绍的Base58字母表中进行编码:

比特币 私钥恢复钱包_比特币私钥格式转换器_比特币钱包导出私钥

图片.png

至此,比特币地址的整个生成过程就完成了。

6.多币种多账户

让同一个seed支持多币种、多账户等,各层定义如下:

m /purpose'/coin_type'/account'/change/address_index

purpose'固定为44',表示使用BIP44。 而coin_type'用来表示不同的货币,比如比特币是0',以太坊是60'。

7. 以太坊确定性钱包

以太坊确定性钱包项目有一个 golang 实现:.

本项目提供的钱包相关接口有:

NewMnemonic(bits): 通过熵生成BIP-39助记词,bits一般等于128位NewSeedFromMnemonic(mnemonic):使用助记词生成BIP-39种子,不带密码
NewSeed(): 不使用助记词,通过rand包直接生成512位长度的BIP-39种子
NewSeedFromMnemonic(mnemonic): 助记词转换为种子
NewFromSeed(seed): 通过种子创建一个新的钱包
NewFromMnemonic(mnemonic): 通过助记词创建一个新的钱包

钱包结构定义:

type Wallet struct {
    mnemonic  string   //助记词(可选)
    masterKey *hdkeychain.ExtendedKey   //主秘钥,包含主私钥和主链码
    seed      []byte  //种子
    url       accounts.URL //HD钱包的生成路径
    paths     map[common.Address]accounts.DerivationPath //每个账户的派生路径
    accounts  []accounts.Account  //保存所有当前钱包的账户
    stateLock sync.RWMutex   //钱包操作锁
}

7.1 生成钱包

比特币 私钥恢复钱包_比特币私钥格式转换器_比特币钱包导出私钥

先生成种子,种子可以直接生成,也可以根据熵和助记词生成。

根据种子,通过hdkeychain.NewMaster(seed)生成主私钥和主链码,对种子进行哈希运算:

HMAC-SHA512(Key = "Bitcoin seed", Data = Seed)

账户派生路径DerivationPath格式如下:

m / purpose' / coin_type' / account' / change / address_index

在:

所以以太坊的根路径是m/44'/60'/0'/0

7.2 钱包界面

Wallet钱包提供的接口如下:

Accounts(): 返回当前钱包所有账户列表
Contains(account): 检查指定账户是否在本钱包里
Unpin(account): 取消固定指定的账户: 从钱包中删除该账户
Derive(path, pin): 根据path派生一个账户地址: path->派生私钥->公钥->地址
PrivateKey(account): 获取账户私钥
PublicKey(account): 获取账户公钥
Address(account): 获取账户地址
Path(account): 账户路径
SignHash(account, hash): 签名hash
SignTx(account, tx, chainID): 签名交易

在钱包界面中,最重要的是导出Derive。

子私钥和子公钥的派生算法使用相同的接口:ExtendedKey.Child(i),派生指定索引的子密钥。 如果输入索引大于0x80000000,则表示导出了增强密钥,否则为普通密钥。

子密钥也是通过以下 HMAC-SHA512 函数获得的:

HMAC-SHA512(Key = chainCode, Data = data)

随附的:

比特币 私钥恢复钱包_比特币钱包导出私钥_比特币私钥格式转换器

图片.png