主页 > imtokenapp下载安装 > 比特币那些事(四)——钱包

比特币那些事(四)——钱包

imtokenapp下载安装 2023-05-05 05:34:26

从比特币私钥恢复钱包_比特币钱包没看到私钥_用私钥和助记词恢复比特币

概述

比特币中的钱包不是传统意义上的钱包。 它不包含比特币,只包含密钥。 每个用户都有一个包含多个密钥的钱包,而钱包只包含一个私钥/公钥对的钥匙串。 用户使用他们的密钥签署交易,从而证明他们拥有交易输出并最终花费比特币。 关于交易输出的概念,可以查看《比特币那些事(二)——交易》。

钱包

比特币钱包根据其包含的多个密钥是否相互关联可分为两种类型:

非确定性钱包中的所有密钥都是从随机数中独立生成的。 钥匙之间没有任何关系,因此也被称为“Just a Bunch of Keys”,简称JBOK钱包。

确定性钱包中的所有密钥都源自一个主密钥。 主密钥也称为种子(Seed)。 确定性钱包中的所有密钥都是相互关联的,如果有原始种子,所有密钥都可以重新生成。

非确定性钱包

在早期的比特币客户端(Bitcoin Core,又称比特币核心客户端)中,钱包只是随机生成的私钥的集合。 随机密钥的缺点是,如果您生成许多私钥,则必须保留所有私钥的副本。 每个密钥都必须备份,否则如果钱包无法访问,钱包控制的资金将丢失。

比特币钱包没看到私钥_用私钥和助记词恢复比特币_从比特币私钥恢复钱包

确定性钱包

确定性钱包使用单向离散函数从公共种子生成私钥。 种子是随机生成的数字。 在确定性钱包中,种子可以恢复所有已经生成的私钥,所以只需要在种子最初创建时进行备份即可。

从比特币私钥恢复钱包_比特币钱包没看到私钥_用私钥和助记词恢复比特币

分层确定性钱包

确定性钱包使用许多不同的密钥派生方法。 最常用的推导方式是采用树状结构,称为Hierarchical Deterministic Wallet(简称HD Wallet)。 在HD钱包中用私钥和助记词恢复比特币,一个父密钥可以衍生出一系列的子密钥,每个子密钥又可以衍生出一系列的孙密钥,以此类推,无限衍生。

用私钥和助记词恢复比特币_比特币钱包没看到私钥_从比特币私钥恢复钱包

例子

下面,我们通过一个例子来介绍HD钱包的实现原理。

爱丽丝经营一家销售 T 恤的在线商店。 她使用 Trezor 比特币硬件钱包(Hardware HD Wallet)来管理她的比特币。

比特币钱包没看到私钥_从比特币私钥恢复钱包_用私钥和助记词恢复比特币

当 Alice 第一次使用 Trezor 时,设备会从内置的硬件随机数生成器生成一个种子短语。 钱包会依次在屏幕上一一显示助记词。 通过注意这些助记符,爱丽丝创建了一个备份,如下所示。

1军队2货车3防御4携带5嫉妒6真

7

垃圾

8个

宣称

9

回声

10

从比特币私钥恢复钱包_比特币钱包没看到私钥_用私钥和助记词恢复比特币

媒体

11

制作

12

紧缩

注意:这里是一个显示 12 个助记词的例子。 事实上,大多数硬件钱包都会生成更安全的 24 位助记词。

工作原则

HD钱包的密钥推导主要包括以下步骤:

下面我们将依次介绍三个主要步骤。

创建助记词

BIP-39 是定义助记词和种子创建的助记词行业标准。 种子短语由钱包使用 BIP-39 中定义的标准化流程自动生成。 助记词的生成主要包括以下步骤:

创建一个128到256位的随机序列(熵) 提取随机序列哈希值(随机序列长度/32)的前几位作为随机序列的校验和 将校验和拼接在随机序列的末尾 分成多个units,每个unit占11位,将每个unit的值映射到一个包含2048(2^11)个词的字典map,得到有序的词组,即助记词

从比特币私钥恢复钱包_用私钥和助记词恢复比特币_比特币钱包没看到私钥

根据以上助记词生成步骤,可以推导出随机序列(熵)与助记词长度的关系,如下表所示:

熵(bits)校验和(bits)熵+校验和(bits)助记符长度(words)

128

4个

132

12

160

5个

165

15

192

6个

198

18

224

比特币钱包没看到私钥_用私钥和助记词恢复比特币_从比特币私钥恢复钱包

7

231

21

256

8个

264

二十四

创建种子

创建助记词后,可以通过密钥扩展函数PBKDF2进一步生成种子。

密钥扩展函数PBKDF2有两个参数:助记符、盐(Salt)。 加盐的目的是增加暴力破解的难度。

种子的生成主要包括以下步骤:

PBKDF2 key stretching函数的第一个参数是助记符。 PBKDF2 密钥拉伸函数的第二个参数是盐。 盐由助记符和可选的用户提供的密码组成。 PBKDF2密钥扩展函数内部使用HMAC-SHA512算法进行2048次哈希运算生成512位种子。

用私钥和助记词恢复比特币_从比特币私钥恢复钱包_比特币钱包没看到私钥

下表显示了将示例 128 位熵转换为 512 位种子的结果。

熵(128 位)0c1e24e5917779d297e14d45f14e1a1a

助记符(12个字)

陆军面包车防御携带嫉妒真正的垃圾索赔回声媒体制造紧缩

密码

(没有任何)

种子(512 位)

5b56c417303faa3fcba7e57400e120a0ca83ec5a4fc9ffba757fbe63fbd77a89a1a3be4c67196f57c39a88b76373733891bfaba16ed27a813ceed498804c0570

创建钱包

创建钱包主要包括以下工作:

下面依次介绍这些步骤。 介绍完我们再来看看安全隐患,接下来介绍硬件子私钥的创建。

创建主私钥

HD钱包的确定性来自于Root Seed,即上述过程生成的种子。

根种子可以通过HMAC-SHA512算法生成一个512位的哈希值。 哈希值分为左右两部分,分别为:

从比特币私钥恢复钱包_比特币钱包没看到私钥_用私钥和助记词恢复比特币

从比特币私钥恢复钱包_用私钥和助记词恢复比特币_比特币钱包没看到私钥

创建子私钥

我们知道 HD 钱包使用树结构进行密钥推导。 在树状密钥结构中,除主密钥通过根种子派生外,其他层级的密钥均通过其父密钥派生,使用子密钥派生函数CKD(Child Key Derivation)。

调用子密钥派生函数需要三个参数:

子私钥的具体推导过程:由父私钥推导出父公钥,结合父公钥-父链码-索引号使用HMAC-SHA512算法结合父私钥生成一个512位散列值。 继续将哈希值拆分为左右两部分,得到:

比特币钱包没看到私钥_用私钥和助记词恢复比特币_从比特币私钥恢复钱包

在上述子密钥派生函数中使用的三个参数中,父私钥和父链码的组合称为Extended Private Key。 通过上面的子私钥推导原理可知,扩展密钥是HD钱包中密钥树的一个分支,可以导出该分支下的所有密钥。

与扩展私钥对应的是扩展公钥(Extended Public Key),它由父公钥和父链码组成,可以通过父公钥直接创建子公钥。

创建子公钥

分层确定性钱包的另一个特点是子公钥可以直接从父公钥推导出来,不需要私钥。 这为我们提供了两种派生子公钥的方法:

子公钥的具体推导过程(使用扩展公钥):结合父公钥-父链码-索引号后,使用HMAC-SHA512算法,结合父公钥生成512位哈希价值。 继续将哈希值拆分为左右两部分用私钥和助记词恢复比特币,得到:

比特币钱包没看到私钥_从比特币私钥恢复钱包_用私钥和助记词恢复比特币

这个子公钥的推导过程不涉及任何私钥。 应用于实际场景时,私钥和公钥可以分开管理。 例如:在电子商务场景中,Web 服务器只维护公钥树形结构,并为每笔交易创建一个比特币地址(它只能接收比特币,不能花费比特币)。 为了安全起见,Web 服务器不会有任何私钥。 电商服务器维护私钥树状结构,确保比特币的消费权掌握在自己手中。

创建加固的子私钥

现在,让我们深入研究如何分别使用扩展私钥和扩展公钥创建上述子私钥和子公钥。 两个扩展键都包含相同的父链代码。 此时可能存在安全隐患:由于扩展公钥包含父链码,如果子私钥泄露,攻击者可以通过扩展公钥的父链码和子链码组成扩展私钥私钥。 那么,这个分支下的所有私钥都会被泄露。 更糟糕的是,子私钥和父链码可以用来推断父私钥。

为了应对这种风险,HD 钱包使用了一种称为强化推导的推导函数。 其本质是使用不同的链码进行子私钥推导和子公钥推导。

具体实现是使用父私钥导出子链码。 非硬化子私钥派生使用父公钥派生子链码。

从比特币私钥恢复钱包_比特币钱包没看到私钥_用私钥和助记词恢复比特币

核心源代码

下面是比特币开源库BitcoinKit中密钥推导的核心方法。 从中,我们可以一窥其技术原理。

// BitcoinKitPrivateSwift.swift
// 子私钥和子公钥的衍生方法源代码
func derived(at childIndex: UInt32, hardened: Bool) -> _HDKey? {
    var data = Data()
    // 是否使用硬化衍生
    if hardened {
        data.append(0)
        guard let privateKey = self.privateKey else {
            return nil

从比特币私钥恢复钱包_比特币钱包没看到私钥_用私钥和助记词恢复比特币

} // 强化衍生时,加入母私钥 data.append(privateKey) } else { // 非强化衍生,加入母公钥 data.append(publicKey) } var childIndex = CFSwapInt32HostToBig(hardened ? (0x80000000 as UInt32) | childIndex : childIndex) // 加入索引号 data.append(Data(bytes: &childIndex, count: MemoryLayout.size)) // 结合母链码,生成哈希值。注意,是否为强化衍生将影响生成的链码 var digest = _Hash.hmacsha512(data, key: self.chainCode) let derivedPrivateKey: [UInt8] = digest[0..<32].map { $0 } // 左半部分为私钥 let derivedChainCode: [UInt8] = digest[32..<64].map { $0 } // 右半部分为链码 var result: Data if let privateKey = self.privateKey { // 子私钥的衍生。调用本方法时会传入 privateKey guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN)) else { return nil } defer { secp256k1_context_destroy(ctx) } // 本质上,使用了母私钥衍生子私钥 var privateKeyBytes = privateKey.map { $0 } var derivedPrivateKeyBytes = derivedPrivateKey.map { $0 } if secp256k1_ec_privkey_tweak_add(ctx, &privateKeyBytes, &derivedPrivateKeyBytes) == 0 { return nil } // 子私钥

从比特币私钥恢复钱包_用私钥和助记词恢复比特币_比特币钱包没看到私钥

result = Data(privateKeyBytes) } else { // 子公钥的衍生。调用本方法时不会传入 privateKey guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_VERIFY)) else { return nil } defer { secp256k1_context_destroy(ctx) } // 本质上,使用了母公钥衍生子公钥 let publicKeyBytes: [UInt8] = publicKey.map { $0 } // 子公钥推导的特殊处理,结合了母公钥 var secpPubkey = secp256k1_pubkey() if secp256k1_ec_pubkey_parse(ctx, &secpPubkey, publicKeyBytes, publicKeyBytes.count) == 0 { return nil } if secp256k1_ec_pubkey_tweak_add(ctx, &secpPubkey, derivedPrivateKey) == 0 { return nil } var compressedPublicKeyBytes = [UInt8](repeating: 0, count: 33) var compressedPublicKeyBytesLen = 33 if secp256k1_ec_pubkey_serialize(ctx, &compressedPublicKeyBytes, &compressedPublicKeyBytesLen, &secpPubkey, UInt32(SECP256K1_EC_COMPRESSED)) == 0 { return nil } // 子公钥 result = Data(compressedPublicKeyBytes) } let fingerPrint: UInt32 = _Hash.sha256ripemd160(publicKey).to(type: UInt32.self) return _HDKey(privateKey: result, publicKey: result, chainCode: Data(derivedChainCode), depth: self.depth + 1, fingerprint: fingerPrint, childIndex: childIndex) }

参考《精通比特币》和《区块链技术指南》BitcoinKit