我希望這篇文章能幫助你理解比特幣的公開金鑰和私鑰,進而讓你更容易理解比特幣的運作原理。讀懂了這篇文章會讓你更容易理解如何透過擲骰子的方式生成助記詞,多簽名錢包的生成以及 PGP 加密。本文源自於 Arman The Parman 的《Understanding Bitcoin Public and Private Keys (3rd Edition)》,由動區專欄作者 以太坊愛好者 整理、編譯與撰稿。
(前情提要:把私鑰儲存在「Evernote」被駭!Tether協助特勤局調查,凍結30萬美元USDT)
(背景補充:什麼支撐了比特幣價值〈三〉挖礦實現真正的自由平等:「比特幣中,不存在身份」)
凡是關心自己的比特幣財產安全的人都應該試著理解下圖:
數字
如果你完全理解二進位、十進位和十六進位,可以跳過這部分。
十進位指每一位數都有 10 種可能(0、1、2、3、4、5、6、7、8 或 9)。數字 「6.15」 有 3 位數(順帶一提,6.15 這個數字是有特殊含義的,即,每個人都應該努力擁有 6.15 個比特幣)。第一個數是 「6」,第二個數是 「1」,第三個數是 「5」。這三個數可以是 0、1、2、3、4、5、6、7、8、9 中的任何一個。
在十進位中,我們從 「0」 數到 「9」(0、1、2、3、4、5、6、7、8、9)就沒有新的數了,接下來就要在左邊新增一位 「1」(即,逢十進一),得到 「10」,然後再從 「0」 開始數起 —— 原先的 「9」 進了一位,十位數變成了 「1」,個位數變成了 「0」。
二進位指每一位元數只有兩種可能(「0」 和 「1」)。
在二進位中,我們從 「0」 數起,然後是 「1」 ,接著就是 「10」!看見沒?如果你覺得跨度很大,那是因為你還沒有跳出十進位思維。
在十進位中,「10」 就是普通人的手指數量 —— 因為我們有 10 個手指,所以我們人類常用 10 進制。
但是,在二進位中,「10」 就是一個人的大拇指的數量。想像一下用你的大拇指數數:1、10、11、100、101、111、1000、1001…… (譯者注:這幾個數字換算成十進位就是 1、2、3、4 ……)
如果這是你第一次瞭解二進位,可以用紙和筆自己算一下,可能會有幫助。這就像是第一次學習數數那樣,而且還不能靠直覺。
十六進位指每一位數有 16 種可能(0、1、2、3、4、5、6、7、8、9、a、b、c、d、e、f)。其中,a = 10、b= 11、c = 12、d = 13、e = 14 和 f = 15。
就像撲克牌遊戲一樣,Jack = 11、Queen = 12、King = 13、Ace = 14 或 1。一個字母可以代表一個數位,這就是關鍵。
每一位數的可能性越多,表達一個大數字所用的位元數就越少。例如,十進位下的 2047 ,寫成二進位就是 11111111111(11 位數),寫成十六進位就是 7FF。
總結:
隨機二進位數字、校驗和私鑰
私鑰首先是一個二進位數字,可以轉換成其它形式。但是,私密金鑰本質上是二進位數字,因為它是為了給電腦使用的。下面是二進位私密金鑰的一個例子:
01000011111 10101110110 01001000001 01001101000 10000100011 10001011011 00100110111 11010000011 11001000001 10111110010 00010101000 00101110110 01100001101 11000010011 01101111001 11001010111 10011010000 01001110000 01000010010 00001110011 10011110101 11000110011 10101101110 00100111111
請注意,這個私鑰由 24 組數字組成,每 11 個數位為一組,共計 264 個二進位數字(24×11=264)。
電腦看到的私密金鑰雖然是同一串數位,但是沒有空格:
010000111111010111011001001000001010011010001000010001110001011011001001101111101000001111001000001101111100100001010100000101110110011000011011100001001101101111001110010101111001101000001001110000010000100100000111001110011110101110001100111010110111000100111111
這是個很大的數字,寫成十進位就是:
7,869,270,257,961,728,227,967,109,454,183,816,220,476,881,432,001,550,169,555,390,346,110,510,455,025,983
請注意這兩個數的值是一樣的,區別只在於十進位寫起來更短。
總之,私密金鑰有一部分是隨機生成的,而最後 8 位(叫作校驗和(checksum))是以前面的隨機部分作為輸入通過一個公式計算得出的。
這是一種(軟體錢包共同認可的)工程設計:如果資料登錄不正確,電腦就會發現校驗和與輸入資料不匹配,並警告用戶。錢包會提示 「抱歉,您的輸入可能有誤」 。當然,用戶可以強行選擇繼續。校驗和並非比特幣代碼的一部分,是為了保障用戶安全而引入的。
換言之……從數學設計上來講,在創建私密金鑰時,下面這個隨機部分……
01000011111 10101110110 01001000001 01001101000 10000100011 10001011011 00100110111 11010000011 11001000001 10111110010 00010101000 00101110110 01100001101 11000010011 01101111001 11001010111 10011010000 01001110000 01000010010 00001110011 10011110101 11000110011 10101101110 001
只會生成下面這個校驗和……
00111111
把二者結合起來就得到了最終的私密金鑰。點擊此處,瞭解私密金鑰是怎麼來的,以及校驗和是如何計算的。
請注意,8 位數的校驗和與末尾 3 個亂數字組合起來剛好是一組 11 位元數字,與其它幾組一樣(在 BIP39 標準下,一個單詞需要 11 位元數表達,詳見下文)。
不同的隨機二進位數字會產生不同的校驗和。假設用戶在錢包裡輸入私密金鑰,並聲稱 「這是我之前生成的私密金鑰,請顯示我的位址」,只要有一位元數位錯誤,軟體錢包都會發現並發出警告。
請原諒我有些囉嗦,但是掌握這些背景知識真的很重要。
私鑰轉換
人類很難準確記錄下一個二進位私密金鑰並將其輸入軟體錢包。一旦發生錯誤,就有可能導致比特幣丟失。手寫無法使用校驗和來檢查錯誤,只有輸入電腦才可以。
一種解決方案是將二進位數字轉化成十進位數字,讓私密金鑰變得更短、更好記。
假設一個二進位數字被切分成每 11 個數字一組,則每一組數最多可以表示 2048 個十進位數字(可表示的十進位數字範圍是 「0」 至 「2047」)。「0」 至 「2047」 轉化成二進位就是 「00000000000」 至 「11111111111」。
我們可以將這個私密金鑰轉換成 24 組十進位數字,每組十進位數字的範圍是 「0」 至 「2047」。這樣寫起來容易,但還是容易出錯。
BIP-39 可以有效化解這一風險。這個協議建議比特幣使用者使用一列由協定定義過的單詞表,我猜測這些單詞是經過精挑細選的,以防被誤讀成其它單詞。
BIP-39 單詞表共包含 2048 個單詞,按照字母順序排列。點擊此處,查看列表。除了英文版,還有其它語言版本。每個單詞都代表 「0」 至 「2047」 之間的某個數字。
這樣一來,私鑰中的每個十進位數字都可以被寫成一個單詞。數字和所對應的單詞之間有什麼特殊聯繫嗎?沒有,這只是由協議定義的,只要我們都使用這個協定,那麼單詞與數位之間就可以畫上等號。
這就是助記詞(seed words)的由來。你在軟體錢包中輸入助記詞後,每個單詞都會轉化成 11 個二進位位元,將它們組合起來就會形成一個 264 位的二進位數字,也就是私密金鑰(還記得嗎?最後一個單詞包含校驗和,因此不是隨機的)。如果是由 12 個單詞組成的助記詞,私密金鑰的長度只有一半,也就是 132 位。
遺憾的是,原始的 BIP-39 單詞表儲存在 GitHub 內,代表的十進位數字範圍是 「1」 至 「2048」,而非 「0」 至 「2047」。這只是 Github 格式的問題,而非有意設計成如此。
為了清楚地說明這個問題,我們來舉個例子。假設某個私密金鑰的開頭是 11 個 「0」,例如 「00000000000」,那麼我們要用單詞表上的第一個單詞來表示這個二進位數字。
第一個單詞是 「abandon」,表示的是 「00000000000」,但是被標記成了 「1」。這是不對的。十進位數字 「1」 轉化成二進位是 「00000000001」,這不是我們想要的。但是,由於格式的問題,單詞表上所有單詞的序號都比它們實際代表的十進位數字大了 「1」。
我們還可以透過拋硬幣來生成二進位數字。電腦可以説明我們獲得使用斜體表示的最後 8 位數:
010000111111010111011001001000001010011010001000010001110001011011001001101111101000001111001000001101111100100001010100000101110110011000011011100001001101101111001110010101111001101000001001110000010000100100000111001110011110101110001100111010110111000100111111
我們首先要做的,是將這個二進位數字按照每 11 個數為一組進行切分:
01000011111 10101110110 01001000001 01001101000 10000100011 10001011011 00100110111 11010000011 11001000001 10111110010 00010101000 00101110110 01100001101 11000010011 01101111001 11001010111 10011010000 01001110000 01000010010 00001110011 10011110101 11000110011 10101101110 00100111111
接下來,我們將每一組數轉化成十進位數字:
543, 1398, 577, 616, 1059, 1115, 311, 1667, 1601, 1522, 168, 374, 781, 1555, 889, 1623, 1232, 624, 530, 115, 1269, 1587, 1390, 319
然後,我們查詢這些十進位數字在單詞表上對應的單詞:
考考你:十進位數字 543 對應的是哪個單詞?Dry、duck 還是 dumb?
上圖是從 Github 上截取的,因此這張單詞表的序號是從 「1」 開始的。
因此,每個序號都減去 「1」 之後才是每個單詞真正對應的十進位數字。因此,序號 544 的單詞 dumb 實際上代表的是 「543」,也就是我們要找的那個單詞。
第二個數是 1398,對應的是單詞表上序號為 1399 的單詞。
全部轉化為單詞後就是:
dumb put else escape love merge cheap spare
sight salad bench conduct giant second hundred
slab old evoke drastic attack pact shoe punch child
請注意,所有單詞都是按字母排序的,首字母越靠前的單詞代表的數位越小,首字母越靠後的單詞代表的數字越大。當你明白這些單詞的排序規律之後,可以明顯看出這點。
擴展私鑰
再來看下面這張圖:
擴展私鑰是使用二進位私鑰以及密語(passphrase)和衍生路徑(derivation path),根據你我都沒必要知道的數學公式計算得出的。
請注意增加密語是如何徹底改變下游擴展私鑰的。修改衍生路徑也會改變下游資料。不要小看了你的軟體錢包提供的預設衍生路徑,請務必把它寫下來保存好。我會另外寫一篇文章來詳細介紹衍生路徑。
擴展私密金鑰最終用來生成一個錢包中的所有比特幣地址,而且可以花費這些地址上的比特幣。如上圖所示,擴展私鑰可以生成多個獨立私鑰(不是擴展私鑰,而是普通的私鑰),每個私鑰會生成獨立的公開金鑰,每個公開金鑰又會生成一個位址。
擴展私鑰還可以用來生成接下來要詳細討論的擴展公開金鑰。
我們無法根據單個獨立私密金鑰倒推出擴展私密金鑰。這是我個人的理解,但是我不是密碼學專家,因此不能確定,不過這麼想很合理。但是,每個獨立私密金鑰有可能指向後一個獨立私密金鑰,當然這點我也不確定。因此,為確保萬無一失,別向任何人洩漏你的任何一個私密金鑰。
可以確定的是,任何一個公開金鑰都不會洩漏其對應的私密金鑰。明白這一點很重要。
我在 https://iancoleman.io/bip39/ 上生成了一個測試錢包。這個網站是練習生成虛擬錢包的好地方(千萬別在聯網的電腦上使用這種方式生成真的錢包)。
擴展私鑰如下所示:
請注意,上圖顯示的是 「帳戶」 擴展私鑰。我不知道這個網站為什麼要這麼標記。
再來看這個擴展私密金鑰的開頭是 「x」。這意味著使用這個私密金鑰將生成以 「1」 開頭的傳統地址。傳統地址也叫作 P2PKH(pay to public key hash,支付到公開金鑰雜湊值)地址。
延伸閱讀:你的「紙錢包」可能不安全!私鑰盜竊問題叢生,資安新創 CYBAVO 詳列危險清單
使用以 「y」 開頭的私鑰(或公開金鑰)會生成以 「3」 開頭的地址。這些位址也叫作 P2SH(Pay to script hash,支付到腳本雜湊值)地址。
使用以 「z」 開頭的私鑰會生成原生的 segwit / Bech32 地址。這類地址以 「bc1q」 開頭。
最後,以大寫字母 「 X」、「Y」、「Z」 開頭的私密金鑰會生成多簽錢包的地址。
擴展公開金鑰
擴展公開金鑰的用途不是很明顯。如果你仔細看示意圖的底部,你會發現使用擴展公開金鑰通過錢包軟體生成的比特幣位址與使用擴展私鑰生成的一樣,而且位址順序相同。使用擴展公開金鑰和擴展私鑰生成的錢包看起來完全一樣。那麼區別在哪兒呢?
使用擴展私鑰生成的錢包能夠進行支付。
使用公開金鑰生成的錢包無法進行支付。這種錢包通常被稱為 「觀察」 錢包。你可以將這個錢包放在安全性低的電腦上,不用擔心會丟失私鑰,但可以用來查看你的錢包餘額,或是複製位址發送給其他人。
但你還是應該注意保護好自己的擴展公開金鑰。一旦洩漏,其他人就可以通過訪問你的擴展公開金鑰來查詢你的錢包餘額和你的所有地址。從今往後,他們都可以查詢你的錢包餘額,就好像查詢你的銀行帳單一樣。
保護好你的金融隱私,更要保護好你的金融金鑰(比特幣私鑰)。
注:擴展公開金鑰如下圖所示:
如上圖所示,公開金鑰不是以 「xprv」 開頭,而是以 「xpub」 開頭,另外也可以 「ypub」、「zpub」、「Xpub」、 「Ypub」 和 「Zpub」 開頭。(大寫指的是多簽私鑰。)
總結
我希望這篇文章能幫助你理解比特幣的公開金鑰和私鑰,進而讓你更容易理解比特幣的運作原理。
如果你有任何問題,請聯繫我,或閱讀我的輔導材料。
讀懂了這篇文章會讓你更容易理解如何透過擲骰子的方式生成助記詞,多簽錢包的生成以及 PGP 加密。
📍相關報導📍
新手科普 | 加密貨幣資產安全:你的「比特幣」真的完全屬於你嗎?
必讀乾貨|企業導入區塊鏈面臨的第一個問題:私鑰管理 (資安)
讓動區 Telegram 新聞頻道再次強大!!立即加入獲得第一手區塊鏈、加密貨幣新聞報導。
LINE 與 Messenger 不定期為大家服務