基於 BIP-32 和 BIP-39 規範生成 HD 錢包(分層確定性錢包)
關於 Bitcoin 的錢包
在 Bitcoin 中有兩種主要型別的錢包,分別為:
- 非確定性錢包(Nodeterministic Wallet) :該錢包中的每個金鑰都是從不同的隨機數獨立生成的,金鑰彼此之間沒有任何關係,這種錢包也被稱為 JBOK 錢包(Just a Bunch Of Keys);
- 確定性錢包(Deterministic Wallet) :其中所有的金鑰都是從一個主金鑰派生出來的,這個主金鑰就是種子(seed),在該型別的錢包中,所有的金鑰之間都是相互關聯的,如果有原始種子,就可以再次生成全部的金鑰;在確定性錢包中,可以使用不同的金鑰推導方式。目前最常用的推導方法是 樹狀結構,也稱為 分層確定性錢包 或 HD 錢包 ;
我們文章的目的就是生成 HD 錢包 , HD 錢包 可以允許使用者在不安全的伺服器上使用,或者在每筆交易中使用不同的公鑰。
涉及到錢包的一些常用標準規範有:
要生成錢包私鑰,我們需要有個種子(seed),而我們這裡是通過助記詞來生成的,下面我們就先了解一下如何基於 BIP-39 規範來生成助記詞和種子(seed)。
關於 BIP-39
BIP-39 規範主要描述了基於助記詞(一組便於記憶的單詞)來生成確定性錢包的演算法和過程。
該規範中主要由兩部分構成:
- 如何生成助記詞;
- 如何將生成的助記詞轉化成一個二進位制種子;
下面就先分別介紹這兩個部分來看看如何生成確定性錢包。
後面涉及到一些示例程式碼都是採用的 python-mnemonic 庫。
生成助記詞
生成助記詞的演算法過程如下圖:

具體過程如下:
- 建立一個 128 到 256 位(步長 32 位)的隨機序列(熵);
隨機序列的長度稱為 熵長,熵長按照步長 32 位,主要有幾種分別為 [128, 160, 192, 224, 256],我們示例圖中是 128 位;
- 對上一步生成的隨機序列進行 SHA256 生成 Hash 值,並取出該 Hash 值的前 N 位(熵長/32,如:128 位,則 N = 4)作為隨機序列的校驗和(Checksum);
- 將 Checksum 新增至第一步生成的隨機序列的尾部,此時對於圖中示例加上 Checksum 之後為 128 + 4 = 132 位的隨機序列;
- 將上一步的隨機序列按照 11 位一段進行分隔(split),這樣對於 128 位熵長的序列就會生成 12 段(132/11=12);
- 此時將每個包含 11 位部分的值與一個預定義的 2048 個單詞的詞典進行對應;
- 按照切割順序生成了最終的單片語就是助記詞;
可以看到不同熵長對應的 Checksum 的長度,最終生成的助記詞的長度不同,具體如下表:
Entropy(bits) | Checksum(bits) | Entropy+Checksum(bits) | Mnemonic length(words) |
---|---|---|---|
128 | 4 | 132 | 12 |
160 | 5 | 165 | 15 |
192 | 6 | 198 | 18 |
224 | 7 | 231 | 21 |
256 | 8 | 264 | 24 |
上面第 5 步涉及到單詞表,理想的單詞表應該滿足智慧選詞、避免相似單詞、排序單詞表等特點,目前支援了多種 不同國家的單詞表 。
如下示例程式碼基於 128 位強度的熵長生成了 12 個助記詞:
>>> from mnemonic import Mnemonic >>> m = Mnemonic('english') >>> words = m.generate(strength=128) >>> words u'olympic hard body window sibling used only mimic put sad ability bone' >>>
從助記詞生成種子
助記詞生成之後我們可以通過金鑰生成函式 PBKDF2 演算法來生成種子。
PBKDF2需要提供兩個引數:助記詞和鹽(salt)。其中 salt 的目的就是增加破解難度,而在 BIP-39 中,我們可以引入密碼(passphrase)來作為保護種子的附加安全因素。
PBKDF2 is part of RSA Laboratories’ Public-Key Cryptography Standards (PKCS) series, specifically PKCS #5 v2.0, also published as Internet Engineering Task Force’s RFC 2898.
接著上面的助記詞生成之後,如下圖為生成 seed 的演算法過程:

- PBKDF2 的第一個引數是上面生成的助記詞;
- PBKDF2 的第二個引數就是 salt,一般有字串和可選的使用者提供的密碼字串連線組成;
- PBKDF2 使用 HMAC-SHA512 演算法,使用了 2048 次 Hash 之後產生一個 512 位的值作為種子;
如下示例程式碼為基於上面示例中生成的助記詞來生成種子:
>>> seed = Mnemonic.to_seed(words, "hellobtc") >>> seed "\xb8\x94\xc79\xc6v\x07VY:\xfd\xb9J\x1d)\xffu3\x0c\x1d'\xd1F\xed\xe5c{R\xb9M\xdbu+\xdc\xc3\xb7\xc34\xe0\x81\xca\x97\x98W\xcf\xab\xa6\xa4c\xf3\xc9\x1d\xc0\xee\xd2\xa2{\xdaX+\x82\x14R\xfa" >>> base58.b58encode(seed) '4h3QDYvyXEZRFeoCztcMybKH4aXkysTEmNqyDG2ZUyLTGvGwWUxXcEefCEB5JYjE8zuh2MSmLKsz9e8SQDpmzhuB' >>> base58.b58encode_check(seed) 'R9cTcYjTpLGZEKquHyv5MzyfQAEYyRzAFTd9dxqNhKqKCCKsxmwcy27qetTbK8zEZDzLSLf7AjF9L9cuWY6bZ4UGzZ3GQ' >>>
從種子開始生成 HD 錢包
下面就將上面生成的種子作為 HD 錢包的根種子(root seed),任何 HD 錢包的根種子都可以重新創造整個 HD 錢包。

將 root seed 輸入到 HMAC-SHA512 演算法中可以得到一個 512 位的 Hash,該 Hash 的左邊 256 位作為 主私鑰 m(Master Private Key),右邊 256 位作為 主鏈碼(Master Chain Code)。之後的 主公鑰 M(Master Public Key,264 bits)可以通過 主私鑰 m 生成。
各個子層級的金鑰生成規則如下圖:

從上圖可以看到,HD 的金鑰生成如下幾個引數:
- Parent Private Key 或 Parent Public Key ;(均為未壓縮的 256 bits 的 ECDSA 金鑰);
- 256 bits 的 Parent Chain Code ;
- 32-bit 整型的 index number (索引號);
另外,上面的過程是可以遞迴下去的,圖中的 Child Private Key 可以作為其下一層級的 Parent Private Key 。
通過將 (Parent Publick Key, Parent Chain Code, Index Number) 輸入至 HMAC-SHA512 演算法中,我們就可以生成其子金鑰,並且我們可以通過調整 Index Number 來生成同一層級的多個子金鑰。
HD Wallet 的分層金鑰生成結構圖如下:

參考來源
- Master Bitcoin 2nd
- Bitcoin developer guide
- BIP-39 Mnemonic code for generating deterministic keys
- BIP-32 Hierarchical Deterministic Wallets
- BIP-43 Purpose Field for Deterministic Wallets
- BIP-44 Multi-Account Hierarchy for Deterministic Wallets
- PBKDF2 - Password-Based Key Derivation Function 2
- HMAC - hash-based message authentication code