Skip to content

Latest commit

 

History

History
844 lines (609 loc) · 62.5 KB

第四章.asciidoc

File metadata and controls

844 lines (609 loc) · 62.5 KB

金鑰和地址

你可能聽說過比特幣是基於 密碼學 的,它是電腦保安領域廣泛使用的數學分支。密碼學在希臘文中的意思是“祕密寫作”,但密碼學的科學不僅僅包含祕密寫作,它被稱為加密。密碼學也可以用來在不洩露保密內容的情況下,證明一個人知道保密內容(數位簽章),或證明資料的真實性(數位指紋)。這些密碼學基礎是比特幣的關鍵數學工具,並廣泛用於比特幣應用。諷刺的是,加密並不是比特幣的重要組成部分,因為它的通訊和交易資料沒有加密,也不需要透過加密保護資金。在本章中,我們將介紹比特幣中使用的密碼學,以金鑰,地址和錢包的形式控制資金的所有權。

簡介

比特幣的所有權透過 數位金鑰 (digital keys), 比特幣地址 (bitcoin addresses) 和 數位指紋 (digital signatures) 建立。數位金鑰實際上並不儲存在網路中,而是由使用者建立並存儲在檔案或稱為 錢包(wallet) 的簡單資料庫中。使用者錢包中的數位金鑰完全獨立於比特幣協議,可以由使用者的錢包軟體產生和管理,無需參考區塊鏈或訪問網際網路。金鑰支撐了比特幣的許多有趣特性,包括去中心化的信任和控制,所有權證明以及有密碼學保障的安全模型。

為了包含在區塊鏈中,大多數比特幣交易需要有效的數位簽章,這些交易只能使用金鑰產生;因此,任何擁有該金鑰副本的人都可以控制這些比特幣。 用於花費資金的數位簽章也被稱為 證據 (witness),是密碼學中的術語。比特幣交易中的證據證明了所花費資金的真實所有權。

金鑰由一對公鑰和私鑰組成。將公鑰視為類似於銀行帳號,將私鑰視為PIN或支票上的簽名,用於控制帳戶。比特幣使用者很少看到這些數位金鑰。大多數情況下,它們儲存在錢包檔案中並由比特幣錢包軟體管理。

在比特幣交易的付款部分,收款人的公鑰透過其數位指紋表示,稱為 比特幣地址 (bitcoin address),與支票上的收款人姓名一樣使用(即“付款到誰的帳戶”)。大多數情況下,比特幣地址是從公鑰產生的並且對應於公鑰。但是,並非所有的比特幣地址都代表公鑰;他們也可以代表其他受益者,如指令碼,我們將在本章後面看到。透過這種方式,比特幣地址可以抽象為資金接收者,這使交易目的地變得靈活,類似於紙質支票:可用於支付個人帳戶,支付公司帳戶,支付帳單或兌換現金。比特幣地址是金鑰的唯一展現形式,使用者常會看到,因為他們需要向世界公開。

首先,我們將介紹密碼學並解釋比特幣中使用的數學。接下來,我們將看看金鑰是如何產生,儲存和管理的。我們將看一下用於表示私鑰公鑰,地址和指令碼地址的各種編碼格式。最後,我們將看看金鑰和地址的進階用法:虛榮(Vanity),多重簽名,指令碼地址和紙錢包。

公鑰加密和加密貨幣

公鑰密碼技術發明於20世紀70年代,是計算機和資訊保安的數學基礎。

公鑰密碼技術發明後,發現了一些合適的數學函式,例如素數指數運算和橢圓曲線乘法。這些數學函式實際上是不可逆的,這意味著它們很容易在一個方向上計算,但在相反方向上計算是不可行的。基於這些數學函式,密碼學可以建立數位金鑰和不可偽造的數位簽章。比特幣使用橢圓曲線乘法作為其密碼學的基礎。

在比特幣中,我們使用公鑰密碼技術來建立一個控制比特幣訪問的金鑰對。金鑰對由一個私鑰和從它派生的一個唯一的公鑰組成。公鑰用於接收資金,私鑰用於簽署交易以支付資金。

公鑰和私鑰之間存在數學關係,可以用私鑰產生一個訊息的簽名,然後使用公鑰在不公開私鑰的情況下驗證簽名。

在消費比特幣時,當前比特幣的擁有者需要在交易中提供他的公鑰和簽名(每次都不同,但由相同的私鑰建立)。透過公鑰和簽名,比特幣網路中的每個人都可以驗證該交易的有效性並接受,從而確認轉讓這筆比特幣的人擁有它們。

Tip

在大多數錢包實現中,為了方便起見,私鑰和公鑰一起儲存為 金鑰對兒(key pair) 。由於可以從私鑰計算公鑰,因此只儲存私鑰也是可能的。

私鑰和公鑰

比特幣錢包包含金鑰對兒的集合,每個金鑰對兒包含一個私鑰和一個公鑰。私鑰(k)是一個數字,通常隨機選取。我們使用橢圓曲線乘法(單向加密函式)透過私鑰產生公鑰(K)。從公鑰(K)中,我們使用單向加密雜湊函式來產生比特幣地址(A)。在本節中,我們將開始產生私鑰,檢視用於將其轉換為公鑰的橢圓曲線數學運算,最後從公鑰產生一個比特幣地址。私鑰,公鑰和比特幣地址之間的關係如Private key, public key, and bitcoin address所示。

privk_to_pubK_to_addressA
Figure 1. Private key, public key, and bitcoin address
為什麼使用非對稱加密 (公鑰私鑰)?

為什麼在比特幣中使用非對稱加密技術?因為它不是用來“加密”(保密)交易的。相反,非對稱加密的有用特性是產生數位簽章。私鑰可用於為交易產生指紋(數位簽章)。這個簽名只能由知道私鑰的人制作。但是,任何有權訪問公鑰和交易指紋的人都可以使用它們來驗證簽名確實是私鑰的擁有者產生的。非對稱加密的這一有用特性使任何人都可以驗證每筆交易的每個簽名,同時確保只有私鑰擁有者才能產生有效的簽名。

私鑰

私鑰只是一個隨機選取的數字。對私鑰的所有權和控制權是使用者控制相應比特幣地址所關聯的所有資金的根本。私鑰用於透過證明交易中使用的資金的所有權來建立消費比特幣所需的簽名。私鑰在任何時候都必須保密,因為向第三方透露它相當於讓它們控制由該金鑰保護的比特幣。私鑰還必須備份和防止意外遺失,因為如果遺失了私鑰,它就無法恢復,並且它所保護的資金也會永遠遺失。

Tip

比特幣私鑰只是一個數字。你可以使用硬幣,鉛筆和紙隨機挑選你的私鑰:投擲硬幣256次,就可以獲得隨機的一串二進位制(0和1)數字,在錢包中使用。然後可以從私鑰產生公鑰。

透過隨機數產生私鑰

產生金鑰的第一步也是最重要的一步是找到一個安全的熵源或隨機數。建立比特幣私鑰本質上與“選擇一個1到2256之間的數字”相同。只要保證不可預測性和不可重複性,用於選擇該數字的確切方法並不重要。比特幣軟體使用底層作業系統的隨機數產生器產生256位的私鑰(隨機數)。通常,作業系統隨機數產生器是由一個人為的隨機源進行初始化的,這就是為什麼你可能會被要求將滑鼠擺動幾秒鐘。

更具體地來說,私鑰可以是 0 到 n-1 的任何數字,這裡n是一個常數 (n = 1.1578 * 1077, 略小於 2256) 定義為比特幣中使用的橢圓曲線的階數 (see 橢圓曲線密碼學解釋)。為了建立這樣的金鑰,我們隨機選擇一個256位的數字並檢查它是否小於+n+。從程式設計的角度說,通常是透過從密碼學安全的隨機源收集的大量隨機數輸入SHA256雜湊演算法中,該演算法將產生256位的數字。如果結果小於+n+,我們就找到了一個合適的私鑰。否則,我們只需使用另一個隨機數再次嘗試。

Warning

不要自己編寫程式碼或使用你的程式語言提供的“簡單”隨機數產生器來建立一個隨機數。使用密碼學安全的偽隨機數產生器(CSPRNG)和來自足夠熵源的種子。研究你選擇的隨機數產生器函式庫的文件,以確保其是密碼學安全的。正確實施CSPRNG對於金鑰的安全至關重要。

以下是以十六進位制格式顯示的隨機產生的私鑰(k)(256位,顯示為64個十六進位制數字,每個4位):

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD
Tip

比特幣私鑰的數值空間的大小(2256)是非常大的數目。十進位制大約是1077。可見的宇宙估計含有1080原子。

要使用 Bitcoin Core 客戶端產生新的金鑰 (see [ch03_bitcoin_client]), 可以用 getnewaddress 命令。出於安全考慮,它只顯示公鑰,而不顯示私鑰。可以使用 dumpprivkey 命令要求 bitcoind 公開私鑰。dumpprivkey 命令以Base58 checksum編碼顯示私鑰,稱為 錢包匯入格式(WIF),我們將在私鑰格式中更詳細地介紹。以下是使用這兩個命令產生和顯示私鑰的範例:

$ bitcoin-cli getnewaddress
1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
$ bitcoin-cli dumpprivkey 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

dumpprivkey 命令開啟錢包並提取由 getnewaddress 命令產生的私鑰。除非它們都儲存在錢包中,否則+bitcoind+不可能透過公鑰知道私鑰。

Tip

dumpprivkey+命令不會透過公鑰產生私鑰,因為這是不可能的。該命令只是顯示錢包已知的由 getnewaddress+命令產生的私鑰。

你還可以使用比特幣資源管理器命令列工具(參見[appdx_bx])使用命令 seed,ec-new 和 ec-to-wif 來產生和顯示私鑰:

$ bx seed | bx ec-new | bx ec-to-wif
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

公鑰

公鑰使用私鑰透過橢圓曲線乘法計算,這是不可逆的:K = k * G,其中 k 是私鑰,G 是一個稱為 產生點(generator point) 的固定的點,K 是公鑰。如果你知道 K ,那麼稱為“尋找離散對數”的逆運算與嘗試所有可能的 k 值(即蠻力搜尋)一樣困難。在我們示範如何從私鑰產生公鑰之前,我們先來看一下橢圓曲線加密。

Tip

橢圓曲線乘法是密碼學家稱為“陷阱門”的一種函式:在一個方向(乘法)很容易做到,而在相反方向(除法)不可能做到。私鑰的擁有者可以很容易地建立公鑰,然後與世界共享,因為知道沒有人能夠反轉該函式並從公鑰計算私鑰。這種數學技巧成為證明比特幣資金所有權的不可偽造且安全的數位簽章的基礎。

橢圓曲線密碼學解釋

橢圓曲線密碼術是一種基於離散對數問題的非對稱或公鑰密碼技術,用橢圓曲線上的加法和乘法表示。

An elliptic curve 是一個橢圓曲線的範例,與比特幣使用的類似。

ecc-curve
Figure 2. An elliptic curve

比特幣使用由美國國家標準與技術研究院(NIST)建立的稱為 secp256k1 的標準中定義的特定橢圓曲線和一組數學常數。secp256k1 曲線由以下函式定義,產生一個橢圓曲線:

\[\begin{equation} {y^2 = (x^3 + 7)}~\text{over}~(\mathbb{F}_p) \end{equation}\]

or

\[\begin{equation} {y^2 \mod p = (x^3 + 7) \mod p} \end{equation}\]

mod p (模素數p) 表明該曲線位於素數階的有限域上。p, 也寫作 \(\( \mathbb{F}_p \)\), p = 2256 – 232 – 29 – 28 – 27 – 26 – 24 – 1, 是一個非常大的素數.

因為這條曲線是在有限的素數階上而不是在實數上定義的,所以它看起來像是一個散佈在二維中的點的模式,難以視覺化。然而,運算與實數上的橢圓曲線的是相同的。作為範例,Elliptic curve cryptography: visualizing an elliptic curve over F(p), with p=17在一個更小的素數階17的有限域上顯示了相同的橢圓曲線,顯示了一個網格上的點的圖案。可以認為+secp256k1+比特幣橢圓曲線是一個不可思議的大網格上的非常複雜的點陣。

ecc-over-F17-math
Figure 3. Elliptic curve cryptography: visualizing an elliptic curve over F(p), with p=17

例如,以下是座標為(x,y)的點P,它是 secp256k1 曲線上的一個點:

P = (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)
Example 1. Using Python to confirm that this point is on the elliptic curve
Python 3.4.0 (default, Mar 30 2014, 19:23:13)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
>>> x = 55066263022277343669578718895168534326250603453777594175500187360389116729240
>>> y = 32670510020758816978083085130507043184471273380659243275938904335757337482424
>>> (x ** 3 + 7 - y**2) % p
0

在橢圓曲線中,有一個叫做“無限點”的點,它大致相當於零點的作用。在計算機上,它有時用x = y = 0表示(它不滿足橢圓曲線方程,但它是一個容易區分的情況)。

還有一個 + 運算子, 稱為 "加法",與傳統的實數加法有類似的屬性。給定橢圓曲線上的點 P1 和 P2,則 P3 = P1 + P2, 也在橢圓曲線上.

幾何上來說,P3是透過在P1和P2之間畫一條直線來計算的。這條線將在另外一點與橢圓曲線相交,稱此點為 P3' = (x, y)。然後在x軸上反射得到 P3 =(x,-y)。 有幾個特殊情況解釋了“無限點”的需要。

如果 P1 和 P2 是同一點,則 P1 和 P1 之間的直線應該延伸到曲線上 P1 的切線。該切線恰好與曲線相交於一個新的點。你可以使用微積分技術來確定切線的斜率。儘管我們侷限在具有兩個整數座標的曲線上,但這些機制仍然可以神奇的運轉。

在某些情況下(如 P1 和 P2 具有相同的x值但不同的y值),切線將是垂直的,在這種情況下 P3=“無限點”。

如果 P1 是“無窮遠點”,則 P1 + P2 = P2。同樣,如果 P2 是無窮遠點,則 P1 + P2 = P1。這展示了無窮遠點如何扮演零的角色。

+ 是可結合的,這意味著(A + B)+ C = A +(B + C)。這意味著我們可以書寫 A + B + C,沒有括號也沒有歧義。

現在我們已經定義了加法,我們可以用擴充套件加法的標準方式來定義乘法。對於橢圓曲線上的點P,如果k是整數, 則 kP = P + P + P + …​ + P (k 次). 在這種情況下,k有時會被混淆地稱為“指數”。

產生公鑰

從一個隨機產生的私鑰 k 開始,我們將它乘以曲線上的一個預定點,稱為 產生點(generator point) G,以在曲線上的其他位置產生另一個點,這是相應的公鑰 K 。產生點被指定為 secp256k1 標準的一部分,並且對於比特幣中的所有金鑰都是相同的:

\[\begin{equation} {K = k * G} \end{equation}\]

其中 k 是私鑰, G 是產生點, K 是產生的公鑰,即曲線上的一個點。由於所有比特幣使用者的產生點始終相同,因此_G_乘以_G_的私鑰始終會產生相同的公鑰_K_。kK 之間的關係是固定的,但只能從 kK 的一個方向進行計算。這就是為什麼比特幣地址(從 K 派生)可以與任何人共享,並且不會洩露使用者的私鑰( k )。

Tip

私鑰可以轉換為公鑰,但公鑰不能轉換回私鑰,因為計算是單向的。

實現橢圓曲線乘法,我們將先前產生的私鑰 k 與乘法產生點G相乘得到公鑰 K

K = 1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD * G

公鑰_K_被定義為一個點 K = (x,y)

K = (x, y)

其中,

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

為了視覺化一個點與整數的乘積,我們將使用簡單的橢圓曲線來代替實數 — 記住,演算法是相同的。我們的目標是找到產生點 G 的多個 kG ,這與將 G 自身相加 k 次相同。在橢圓曲線中,一個點自身相加相當於在該點上繪製切線並找到它再次與曲線相交的位置,然後在x軸上反射該點。

Tip

大多數比特幣實現使用 OpenSSL cryptographic library 進行橢圓曲線運算。例如,可以使用 EC_POINT_mul() 函式產生公鑰。

ecc_illustrated
Figure 4. Elliptic curve cryptography: visualizing the multiplication of a point G by an integer k on an elliptic curve

比特幣地址

比特幣地址是一串數字和字元,可以與任何想要向你匯款的人分享。從公鑰產生的地址由一串數字和字母組成,從數字“1”開始。以下是一個比特幣地址的例子:

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

比特幣地址是交易中最常見的資金“接收者”地址。如果我們將比特幣交易與紙質支票進行比較,那麼比特幣地址就是受益人,這是“支付給誰”後面要填寫的。對紙質支票來說,受益人有時可以是銀行帳戶的持有人,但也可以包括公司,機構甚至現金。由於紙質支票不需要指定帳戶,而是使用抽象名稱作為資金的接收者,所以它們是非常靈活的支付工具。比特幣的交易使用類似的抽象:比特幣地址,從而使它們非常靈活。比特幣地址可以表示私鑰/公鑰對兒的擁有者,也可以表示其他內容,比如付款指令碼,我們將在[p2sh]中看到。現在,讓我們來看一個簡單的例子,一個代表公鑰的比特幣地址。

比特幣地址是由公鑰單向加密雜湊而來的。“雜湊演算法”是一種單向函式,可以為任意大小的輸入產生指紋或“雜湊”。加密雜湊函式廣泛用於比特幣:比特幣地址,指令碼地址和挖礦PoW驗證演算法。用於從公鑰產生比特幣地址的演算法是“安全雜湊演算法”(SHA)和“RACE完整性基元評估訊息摘要演算法”(RIPEMD),具體來說是SHA256和RIPEMD160。

從公鑰 K 開始,我們計算它的SHA256雜湊值,然後再計算結果的RIPEMD160雜湊值,產生一個160位(20位元組)的數字:

\[\begin{equation} {A = RIPEMD160(SHA256(K))} \end{equation}\]

其中 K 是公鑰,A 是產生的比特幣地址。

Tip

比特幣地址與公鑰不一樣。比特幣地址是使用單向函式從公鑰匯出的。

比特幣地址幾乎總是被編碼為“Base58Check”(參見Base58 和 Base58Check 編碼),該地址使用58個字元(Base58數字系統)和校驗和來提供可讀性,避免模糊不清,防止地址轉錄和輸入中的錯誤。Base58Check也可用在其他需要使用者閱讀並正確轉錄數字(比如比特幣地址,私鑰,加密金鑰或指令碼雜湊)的地方。在下一節中,我們將研究Base58Check編碼和解碼的機制以及由此產生的表示。Public key to bitcoin address: conversion of a public key into a bitcoin address 說明了公鑰轉換為比特幣地址的過程。

pubkey_to_address
Figure 5. Public key to bitcoin address: conversion of a public key into a bitcoin address

Base58 和 Base58Check 編碼

為了使用少量的符號,以緊湊的形式展示很長的數字,許多計算機系統使用基數(進位制)高於10的混合字母數字表示。例如,傳統的十進位制系統使用0到9的10個數字,十六進位制使用16個(字母A到F作為六個附加符號)。以十六進位制格式表示的數字比等效的十進位制表示更短。更加緊湊的Base64表示使用26個小寫字母,26個大寫字母,10個數字和另外2個字元(如 "``" 和 “/” )在基於文字的媒體(如電子郵件)上傳輸二進位制資料。Base64最常用於向電子郵件新增二進位制附件。Base58是一種基於文字的二進位制編碼格式,用於比特幣和許多其他加密貨幣。它在緊湊表示,可讀性和錯誤檢測與預防之間提供了平衡。Base58是Base64的一個子集,使用大小寫字母和數字,省略了一些經常被混淆的,或在使用某些字型顯示時看起來相同的。具體來說,相比Base64,Base58沒有0(數字0),O(大寫o),l(小寫L),I(大寫i)和符號“``”和“/”。Bitcoin’s Base58 alphabet 是完整的Base58字母表。

Example 2. Bitcoin’s Base58 alphabet
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

為了增加防範輸入錯誤或轉錄錯誤的額外安全性,Base58Check是有內建錯誤校驗程式碼的Base58編碼格式,經常在比特幣中使用。校驗和是新增到編碼資料末尾的四個位元組。校驗和來自編碼資料的雜湊雜湊值,可用於檢測和防止轉錄和輸入錯誤。當使用Base58Check程式碼時,解碼軟體將計算資料的校驗和並將其與程式碼中包含的校驗和進行比較。如果兩者不匹配,則會引入錯誤並且Base58Check資料無效。這可以防止錯誤的比特幣地址被錢包軟體接收,導致資金損失。

要將資料(數字)轉換為Base58Check格式,我們首先為資料新增一個名為“版本位元組”的字首,以便輕鬆識別編碼資料的型別。例如,比特幣地址的字首為零(十六進位制中的0x00),而編碼私鑰時使用的字首為128(十六進位制中的0x80)。常用的版本字首參見 Base58Check version prefix and encoded result examples

接下來,我們計算“double-SHA”校驗和,在前面的結果(字首和資料)上應用兩次SHA256雜湊演算法:

checksum = SHA256(SHA256(prefix+data))

在產生的32位元組雜湊(hash-of-a-hash)中,我們只取前四個位元組,作為錯誤檢查程式碼或校驗和。將校驗和追加到最後。

結果由三項組成:字首,資料和校驗和。該結果使用前面描述的Base58字母表進行編碼。Base58Check encoding: a Base58, versioned, and checksummed format for unambiguously encoding bitcoin data 展示了Base58Check編碼過程。

Base58CheckEncoding
Figure 6. Base58Check encoding: a Base58, versioned, and checksummed format for unambiguously encoding bitcoin data

在比特幣中,大部分呈現給使用者的資料都是Base58Check編碼的,以使其緊湊,易於閱讀並易於檢測錯誤。Base58Check編碼中的版本字首用於建立容易區分的格式。這些字元使人們很容易得知編碼資料的型別以及如何使用它。例如,Base58Check編碼的比特幣地址以1開頭,Base58Check編碼的金鑰錢包匯入格式(WIF)以5開頭。一些範例版本字首和Base58字元顯示在 Base58Check version prefix and encoded result examples中。

Table 1. Base58Check version prefix and encoded result examples
Type Version prefix (hex) Base58 result prefix

Bitcoin Address

0x00

1

Pay-to-Script-Hash Address

0x05

3

Bitcoin Testnet Address

0x6F

m or n

Private Key WIF

0x80

5, K, or L

BIP-38 Encrypted Private Key

0x0142

6P

BIP-32 Extended Public Key

0x0488B21E

xpub

金鑰格式

私鑰和公鑰都可以用不同的格式表示。即使這些格式看起來不同,但它們的編碼相同。這些格式主要用於使人們輕鬆閱讀和轉錄金鑰而不會引入錯誤。

私鑰格式

私鑰可以用不同的格式表示,所有這些格式都對應於相同的256位數字。Private key representations (encoding formats) 顯示了用於表示私鑰的三種常用格式。不同的格式用在不同的情況。十六進位制和原始二進位制格式在軟體內部使用,很少向用戶顯示。WIF用於在錢包之間匯入/匯出,並經常用於私鑰的 QRCode(二維條碼)表示。

Table 2. Private key representations (encoding formats)
Type Prefix Description

Raw

None

32 bytes

Hex

None

64 hexadecimal digits

WIF

5

Base58Check encoding: Base58 with version prefix of 128- and 32-bit checksum

WIF-compressed

K or L

As above, with added suffix 0x01 before encoding

Example: Same key, different formats 展示了三種編碼形式的私鑰.

Table 3. Example: Same key, different formats
Format Private key

Hex

1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

WIF

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

WIF-compressed

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

所有這些表示都是顯示相同數字和相同私鑰的不同方式。它們看起來不同,但任何一種格式都可以輕鬆轉換為任何其他格式。請注意,“原始二進位制”沒有顯示在Example: Same key, different formats中。

我們使用 Bitcoin Explorer 的 wif-to-ec 命令(參見[appdx_bx])來示範兩個WIF金鑰代表相同的私鑰:

$ bx wif-to-ec 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

$ bx wif-to-ec KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
Base58Check解碼

Bitcoin Explorer 命令(參見[appdx_bx])讓我們很容易透過編寫shell指令碼和命令列“管道”,操作比特幣金鑰,地址和交易。你可以使用Bitcoin Explorer在命令列上解碼Base58Check格式。

我們使用 base58check-decode 命令解碼未壓縮的金鑰:

$ bx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
wrapper
{
    checksum 4286807748
    payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
    version 128
}

結果包含金鑰的資料內容,WIF版本字首128,以及校驗和。

請注意,壓縮金鑰的“資料內容”附加了字尾+01+,表示派生的公鑰將被壓縮:

$ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
wrapper
{
    checksum 2339607926
    payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01
    version 128
}
將十六進位制編碼為Base58Check

要編碼到Base58Check,與上一個命令相對,我們使用Bitcoin Explorer的 base58check-encode 命令(請參見 [appdx_bx] )並提供十六進位制私鑰,以及WIF版本的字首128:

bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd --version 128
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
從十六進位制(壓縮的金鑰)編碼為Base58Check

要將“壓縮”的私鑰(請參見壓縮的私鑰)編碼為Base58Check,要將字尾+01+附加到十六進位制金鑰後面,然後按照之前的方式進行編碼:

$ bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 --version 128
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

產生的WIF-compressed格式以“K”開頭,表示內部的私鑰具有後綴“01”,將僅用於產生壓縮的公鑰(請參閱壓縮的公鑰)。

公鑰格式

公鑰也能以不同的方式呈現,通常是_compressed_或_uncompressed_公鑰。

如之前所見,公鑰是由一對座標+(x,y)組成的橢圓曲線上的一個點。它通常帶有字首+04,後跟兩個256位數字:一個是該點的_x_座標,另一個是_y_座標。字首+04+表示未壓縮的公鑰,+02+或+03+開頭表示壓縮的公鑰。

這是我們先前建立的私鑰產生的公鑰,顯示為座標 x 和 y :

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

這是以520位數字(130十六進位制數字)表示的公鑰,結構為 04 x y :

K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A↵
07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
壓縮的公鑰

壓縮公鑰被引入到比特幣中,以減少交易處理的大小並節省儲存空間。大多數交易包括公鑰,這是驗證擁有者憑證並消費比特幣所需的。每個公鑰需要520位( 字首 + x + y ),每個塊有幾百個交易,每天產生千上萬的交易時,會將大量資料新增到區塊鏈中。

正如我們在公鑰看到的那樣,公鑰是橢圓曲線上的一個點(x,y)。因為曲線表達了一個數學函式,所以曲線上的一個點代表該方程的一個解,因此,如果我們知道_x_座標,我們可以透過求解方程來計算_y_座標 y2 mod p =( x3 + 7 )mod p。這允許我們只儲存公鑰的_x_座標,省略_y_座標並減少金鑰的大小和所需的256位空間。在每次交易中,幾乎減少了50%的尺寸,加起來可以節省大量的資料!

未壓縮的公鑰的字首為+04+,壓縮的公鑰以+02+或+03+字首開頭。讓我們看看為什麼有兩個可能的字首:因為方程的左邊是 y2,所以_y_的解是一個平方根,它可以具有正值或負值。從視覺上來說,這意味著產生的_y_座標可以在x軸的上方或下方。從An elliptic curve中的橢圓曲線圖可以看出,曲線是對稱的,這意味著它在x軸上像鏡子一樣反射。因此,雖然我們可以省略_y_座標,但我們必須儲存_y_的_sign_(正數或負數);換句話說,我們必須記住它高於或低於x軸,因為每個選項代表不同的點和不同的公鑰。當在素數階p的有限域上以二進位制演算法計算橢圓曲線時,_y_座標是偶數或奇數,如前所述,它對應於正/負號。因此,為了區分_y_的兩個可能值,我們儲存一個壓縮公鑰,如果_y_是偶數,則字首為+02+;如果是奇數,則儲存字首為+03+,從而允許軟體從_x_座標正確推匯出_y_座標,並將公鑰解壓為該點的完整座標。Public key compression中說明了公鑰的壓縮。

pubkey_compression
Figure 7. Public key compression

以下先前產生的公鑰,顯示為以264位(66位十六進位制數字)儲存的壓縮公鑰,字首+03+表示_y_座標為奇數:

K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

這個壓縮的公鑰對應於相同的私鑰,表示它是從相同的私鑰產生的。但是,它看起來與未壓縮的公鑰不同。更重要的是,如果我們使用雙雜湊函式( RIPEMD160(SHA256(K) )將此壓縮公鑰轉換為比特幣地址,它將產生一個_不同的_比特幣地址。這可能會造成混淆,因為這意味著單個私鑰可以產生以兩種不同格式(壓縮和未壓縮)表示的公鑰,這兩種格式產生兩個不同的比特幣地址。但是,兩個比特幣地址的私鑰是相同的。

壓縮公鑰正在逐漸成為比特幣客戶端的預設設定,這對減少交易和區塊鏈的規模具有重大影響。但是,並非所有客戶端都支援壓縮的公鑰。支援壓縮公鑰的較新客戶端必須考慮來自不支援壓縮公鑰的較舊客戶端的交易。當錢包應用從另一個比特幣錢包應用匯入私鑰時,這一點尤其重要,因為新錢包需要掃描區塊鏈以查詢與這些匯入的金鑰相對應的交易。比特幣錢包應該掃描哪些比特幣地址?由未壓縮的公鑰產生的比特幣地址,還是由壓縮公鑰產生的比特幣地址?兩者都是有效的比特幣地址,並且可以用私鑰簽名,但它們是不同的地址!

要解決此問題,從錢包中匯出私鑰時,用WIF表示它們在新比特幣錢包中以不同方式實現,表明這些私鑰已用於產生_compressed_公鑰和_compressed_比特幣地址。這允許匯入的錢包區分源自舊的或較新的錢包的私鑰,並分別在區塊鏈中搜尋與未壓縮的或壓縮的公共金鑰對應的比特幣地址的交易。下一節我們來看看更詳細的工作原理。

壓縮的私鑰

諷刺的是,術語“壓縮私鑰”是一種用詞不當,因為當私鑰以"WIF-compressed"的形式匯出時,它實際上比“未壓縮”的私鑰多一個位元組。這是因為私鑰增加了一個位元組的字尾(在Example: Same key, different formats中以十六進位制顯示為01),表示私鑰來自較新的錢包並且應該僅用於產生壓縮的公鑰。私鑰本身並不壓縮,也不能被壓縮。術語“壓縮私鑰”實際上是指“只能從私鑰匯出壓縮的公鑰”,而“未壓縮的私鑰”實際上是指“只能從私鑰匯出未壓縮的公鑰才”。你只應將匯出格式稱為“WIF-compressed”或“WIF”,不要將私鑰本身稱為“壓縮”的以避免進一步混淆。

Example: Same key, different formats 展示了相同的金鑰以 WIF 和 WIF-compressed 格式編碼。

Table 4. Example: Same key, different formats
Format Private key

Hex

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD

WIF

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

Hex-compressed

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD01

WIF-compressed

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

十六進位制壓縮的私鑰格式在結尾處有一個額外的位元組(十六進位制中的01)。雖然Base58編碼的版本字首對於WIF和WIF-compressed格式都是相同的(0x80),但在數字末尾新增一個位元組會導致Base58編碼的第一個字元從5更改為 KL 。可以把它看作Base58相當於數字100和數字99之間的十進位制編碼差異。雖然100是比99更長的一位數字,但它字首是1而不是9。長度的變化會影響字首。在Base58中,隨著數字長度增加一個位元組,字首5變為 KL

請記住,這些格式不可互換使用。在實現壓縮公鑰的新錢包中,私鑰只能以WIF-compressed方式匯出(帶有 KL 字首)。如果錢包是較舊的實現並且不使用壓縮的公鑰,則私鑰只能以WIF形式匯出(帶有字首5)。這裡的目標是嚮導入這些私鑰的錢包發出訊號,告知它是否必須在區塊鏈中搜尋壓縮或未壓縮的公鑰和地址。

如果一個比特幣錢包能夠實現壓縮公鑰,它將在所有交易中使用這些公鑰。錢包中的私鑰將用於派生曲線上的公鑰,並壓縮。壓縮後的公鑰將用於產生比特幣地址用於交易。從實現壓縮公鑰的新錢包中匯出私鑰時,將修改WIF,並在私鑰上新增一個位元組的字尾+01+。由此產生的Base58Check編碼的私鑰稱為“WIF-compressed”,並以字母 KL 開頭,而不是像來自舊錢包的WIF編碼一樣以“5”開頭。

Tip

“壓縮私鑰”是一個誤用!它們沒有被壓縮;相反,WIF-compressed意味著金鑰只能用於派生壓縮的公鑰及其相應的比特幣地址。諷刺的是,一個“WIF-compressed”編碼私鑰多了1個位元組,因為它具有附加的+01+字尾,可以將其與“未壓縮”的區別開來。

用 C++ 實現金鑰和地址

讓我們看一下建立比特幣地址的完整過程,從私鑰到公鑰(橢圓曲線上的一個點),再到雙重雜湊地址,最後是Base58Check編碼。Creating a Base58Check-encoded bitcoin address from a private key 中的C++程式碼顯示了完整的過程。程式碼範例使用了 [alt_libraries] 中介紹的libbitcoin函式庫來提供一些幫助函式。

Example 3. Creating a Base58Check-encoded bitcoin address from a private key
link:code/addr.cpp[role=include]

該程式碼使用預定義的私鑰在每次執行時產生相同的比特幣地址,如 Compiling and running the addr code 所示。

Example 4. Compiling and running the addr code
# Compile the addr.cpp code
$ g++ -o addr addr.cpp -std=c++11 $(pkg-config --cflags --libs libbitcoin)
# Run the addr executable
$ ./addr
Public key: 0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa
Address: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK
Tip

Compiling and running the addr code 中的程式碼從一個_壓縮的_公鑰(參見 壓縮的公鑰)生成了一個比特幣地址 (1PRTT...)。如果你使用未壓縮的公鑰,它會產生不同的比特幣地址 (14K1y...).

用 Python 實現金鑰和地址

Python中最全面的比特幣函式庫 是Vitalik Buterin寫的 pybitcointools。在 Key and address generation and formatting with the pybitcointools library中, 我們使用 pybitcointools 函式函式庫 (imported as "bitcoin") 以各種格式產生和顯示金鑰與地址。

Example 5. Key and address generation and formatting with the pybitcointools library
link:code/key-to-address-ecc-example.py[role=include]

Running key-to-address-ecc-example.py 展示了執行結果。

Example 6. Running key-to-address-ecc-example.py
$ python key-to-address-ecc-example.py
Private Key (hex) is:
 3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa6
Private Key (decimal) is:
 26563230048437957592232553826663696440606756685920117476832299673293013768870
Private Key (WIF) is:
 5JG9hT3beGTJuUAmCQEmNaxAuMacCTfXuw1R3FCXig23RQHMr4K
Private Key Compressed (hex) is:
 3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa601
Private Key (WIF-Compressed) is:
 KyBsPXxTuVD82av65KZkrGrWi5qLMah5SdNq6uftawDbgKa2wv6S
Public Key (x,y) coordinates is:
 (41637322786646325214887832269588396900663353932545912953362782457239403430124L,
 16388935128781238405526710466724741593761085120864331449066658622400339362166L)
Public Key (hex) is:
 045c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ec↵
243bcefdd4347074d44bd7356d6a53c495737dd96295e2a9374bf5f02ebfc176
Compressed Public Key (hex) is:
 025c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ec
Bitcoin Address (b58check) is:
 1thMirt546nngXqyPEz532S8fLwbozud8
Compressed Bitcoin Address (b58check) is:
 14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3

A script demonstrating elliptic curve math used for bitcoin keys 是另外一個例子,使用橢圓曲線計算的 Python ECDSA 函式庫。

Example 7. A script demonstrating elliptic curve math used for bitcoin keys
link:code/ec-math.py[role=include]

Installing the Python ECDSA library and running the ec_math.py script shows the output produced by running this script.

Note

A script demonstrating elliptic curve math used for bitcoin keys 使用 os.urandom, 體現了底層作業系統提供的密碼學安全的隨機數產生器(CSRNG)。警告:根據作業系統的不同,os.urandom 可能無法以足夠的安全性,並且可能不適合產生高品質的比特幣金鑰。

Example 8. Installing the Python ECDSA library and running the ec_math.py script
$ # Install Python PIP package manager
$ sudo apt-get install python-pip
$ # Install the Python ECDSA library
$ sudo pip install ecdsa
$ # Run the script
$ python ec-math.py
Secret:  38090835015954358862481132628887443905906204995912378278060168703580660294000
EC point: (70048853531867179489857750497606966272382583471322935454624595540007269312627, 105262206478686743191060800263479589329920209527285803935736021686045542353380)
BTC public key: 029ade3effb0a67d5c8609850d797366af428f4a0d5194cb221d807770a1522873

進階的金鑰和地址

在下面的章節中,我們將看看高階形式的金鑰和地址,例如加密私鑰,指令碼和多重簽名地址,虛榮地址和紙錢包。

加密私鑰 (BIP-38)

私鑰必須保密,但對私鑰的“保密性”的需求是在實踐中很難實現,因為它與同樣重要的 可用性 安全目標相沖突。當你需要備份私鑰以避免遺失私鑰時,保持私鑰私密性更加困難。儲存在透過密碼加密的錢包中可能是安全的,但該錢包需要備份。有時,使用者需要將金鑰從一個錢包移動到另一個錢包 - 例如,升級或替換錢包軟體。私鑰的備份也可能儲存在紙張上(請參見紙錢包)或外部儲存介質(如USB快閃記憶體驅動器)中。但是如果備份本身被盜或遺失怎麼辦?這些相互衝突的安全目標促成了一種行動式和便捷的加密私鑰的標準,這種加密方式可以被許多不同的錢包和比特幣客戶端理解,透過BIP-38標準化(參見 [appdxbitcoinimpproposals]).

BIP-38 提出了一個通用標準,用密碼對私鑰進行加密,並使用Base58Check對其進行編碼,以便它們可以安全地儲存在備份介質上,在錢包之間安全地傳輸,或儲存在金鑰可能暴露的任何其他情況下。加密標準使用高階加密標準(AES),這是NIST建立的標準,廣泛用於商業和軍事應用的資料加密實現。

BIP-38加密方案將通常使用WIF編碼(字首為“5”的Base58Check字串)的比特幣私鑰作為輸入。此外,BIP-38加密方案需要一個密碼短語,通常由幾個詞或一串複雜的字母數字字元組成。BIP-38加密的結果是以字首+6P+開始的Base58Check加密私鑰。如果你看到一個以+6P+開頭的金鑰,則該金鑰是加密的,需要密碼才能將其轉換(解密)為可用於任何錢包的WIF格式的私鑰(字首+5+)。許多錢包應用程式現在可識別BIP-38加密的私鑰,並提示使用者輸入密碼以解密並匯入金鑰。第三方應用程式,例如非常實用的基於瀏覽器的應用程式 Bit Address (Wallet Details tab), 可以用來解密BIP-38金鑰。

BIP-38加密金鑰最常見的用例是可用於備份私鑰的紙錢包。只要使用者選擇強密碼,帶有BIP-38加密私鑰的紙錢包就非常安全,並且是建立離線比特幣儲存(也稱為“冷儲存”)的好方法。

使用bitaddress.org測試Example of BIP-38 encrypted private key中的加密金鑰,瞭解如何透過輸入密碼來獲取解密的金鑰。

Table 5. Example of BIP-38 encrypted private key

Private Key (WIF)

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

Passphrase

MyTestPassphrase

Encrypted Key (BIP-38)

6PRTHL6mWa48xSopbU1cKrVjpKbBZxcLRRCdctLJ3z5yxE87MobKoXdTsJ

支付給指令碼的雜湊(Pay-to-Script Hash,P2SH)和多重簽名地址

如我們所知,傳統的以數字“1”開頭的比特幣地址,源自公鑰,而公鑰又是透過私鑰產生的。儘管任何人都可以將比特幣傳送到“1”地址,但只能透過提供相應的私鑰簽名和公鑰雜湊來消費比特幣。

以數字“3”開頭的比特幣地址是支付給指令碼的雜湊(P2SH)地址,有時被錯誤地稱為多重簽名或多重地址。它們將比特幣交易的受益人指定為指令碼的雜湊,而不是公鑰的擁有者。該功能在2012年1月由BIP-16提出 (參見 [appdxbitcoinimpproposals]), 正在被廣泛採用,因為它提供了向地址本身新增功能的機會。與將資金“傳送”到傳統的“1”比特幣地址(也稱為付費至公鑰的雜湊(P2PKH))的交易不同,傳送至“3”地址的資金需要的不僅僅是一個公鑰雜湊和一個私鑰簽名作為所有權證明。這些要求是建立地址時在指令碼中指定的,並且對該地址的所有輸入也要按照相同的要求進行設定。

P2SH地址由交易指令碼建立,該指令碼定義誰可以使用交易的輸出(有關更多詳細資訊,請參見[p2sh])。對P2SH地址進行編碼使用與建立比特幣地址時用到的相同的雙重雜湊函式,只是應用於指令碼而不是公鑰:

script hash = RIPEMD160(SHA256(script))

產生的“指令碼雜湊”使用字首5進行Base58Check編碼,導致編碼地址以+3+開頭。P2SH地址的範例: 3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM, 可以使用Bitcoin Explorer的 script-encode, sha256, ripemd160, 和 base58check-encode 命令(參見 [appdx_bx]) 產生:

$ echo \
'DUP HASH160 [89abcdefabbaabbaabbaabbaabbaabbaabbaabba] EQUALVERIFY CHECKSIG' > script
$ bx script-encode < script | bx sha256 | bx ripemd160 \
| bx base58check-encode --version 5
3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM
Tip

P2SH不一定與多重簽名交易相同。P2SH地址_常用來_表示多重簽名指令碼,但它也可能表示其他型別交易的指令碼。

多重簽名地址與P2SH

目前,P2SH功能最常見的實現是多重簽名地址指令碼。顧名思義,底層指令碼需要多個簽名才能證明所有權,才能花費資金。比特幣多重簽名特徵被設計為需要來自總共N個金鑰的M個簽名(也稱為“閾值”),稱為M-N多重簽名,其中M等於或小於N. 例如,[ch01_intro_what_is_bitcoin] 中的咖啡店老闆Bob可以使用一個多重簽名地址,要求屬於他的一把鑰匙和屬於他的妻子的一把鑰匙中的一個簽名,以確保他們中的任何一個簽字可以簽署鎖定到這個地址的一筆交易輸出。這與在傳統銀行中實施的“聯名帳戶”類似,夫妻的任一方都可以花費一個簽名。再例如,網頁設計師Gopesh,可能會為其業務提供2/3的多重簽名地址,確保除非至少有兩個業務合作伙伴簽署交易,否則不會花費任何資金。

我們將在 [transactions] 中探討如何建立花費 P2SH(和多重簽名)地址的資金的交易。

虛榮地址(Vanity Addresses)

虛榮地址是包含人類可讀資訊的有效比特幣地址。例如,1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33 是一個有效的地址,其中包含形成單詞“Love”的字母作為前四個Base-58字母。虛擬地址需要產生並測試數十億個候選私鑰,直到找到具有所需模式的比特幣地址。雖然在虛榮產生演算法中有一些優化,但這個過程主要包括隨機選擇一個私鑰,匯出公鑰,匯出比特幣地址,並檢查它是否符合所需的虛擬模式,重複數十億次,直到匹配被發現。

一旦找到與所需模式相匹配的虛擬地址,擁有者就可以使用從中得到的私鑰來並以與其他地址完全相同的方式使用比特幣。虛擬地址的安全性不低於其他地址。它們依賴於與其他地址相同的橢圓曲線加密(ECC)和SHA。

[ch01_intro_what_is_bitcoin] 中, 我們介紹了在菲律賓開展業務的兒童慈善機構Eugenia。假設Eugenia正在組織比特幣籌款活動,並希望使用虛擬比特幣地址來宣傳籌款活動。Eugenia將建立一個以“1Kids”開頭的虛榮地址來宣傳兒童慈善籌款活動。讓我們看看這個虛榮的地址如何建立,以及它對於Eugenia慈善機構的安全意味著什麼。

產生虛榮地址

認識到比特幣地址僅僅是Base58字母表中的符號代表的數字很重要。搜尋諸如“1Kids”之類別的模式可以被視為搜尋範圍從 1Kids11111111111111111111111111111 到 +1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz + 的地址。該範圍內的地址約有 5829(約1.4 * 10 ^ 51 ^)個,全部以“1Kids”開頭。[table_4-11 ] 顯示了具有字首1Kids的地址範圍。

Table 6. The range of vanity addresses starting with "1Kids"

From

1Kids11111111111111111111111111111

1Kids11111111111111111111111111112

1Kids11111111111111111111111111113

...

To

1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz

讓我們把“1Kids”模式當作數字,看看我們在比特幣地址中可能發現這種模式的概率(參見The frequency of a vanity pattern (1KidsCharity) and average search time on a desktop PC)。一台普通的桌上型電腦個人電腦,沒有任何專門的硬體,可以每秒搜尋約 100000 個金鑰。

Table 7. The frequency of a vanity pattern (1KidsCharity) and average search time on a desktop PC
Length Pattern Frequency Average search time

1

1K

1 in 58 keys

< 1 milliseconds

2

1Ki

1 in 3,364

50 milliseconds

3

1Kid

1 in 195,000

< 2 seconds

4

1Kids

1 in 11 million

1 minute

5

1KidsC

1 in 656 million

1 hour

6

1KidsCh

1 in 38 billion

2 days

7

1KidsCha

1 in 2.2 trillion

3–4 months

8

1KidsChar

1 in 128 trillion

13–18 years

9

1KidsChari

1 in 7 quadrillion

800 years

10

1KidsCharit

1 in 400 quadrillion

46,000 years

11

1KidsCharity

1 in 23 quintillion

2.5 million years

如你所見,即使Eugenia能夠訪問幾千臺電腦,也不能很快建立“1KidsCharity”虛擬地址。每增加一個字元都會將難度增加58倍。超過7個字元的模式通常由專用硬體尋找,例如具有多個GPU的訂製桌面電腦。這些往往是用於比特幣挖礦的“鑽井平臺”,為比特幣不再適合盈利,但可用於找到虛榮地址。GPU系統上的虛度搜尋速度比通用CPU上的快很多個數量級。

找到虛榮地址的另一種方法是將工作外包給一個虛榮礦工池,例如 Vanity Pool。這是一項服務,允許那些使用GPU硬體的人為其他人搜尋比特幣虛擬地址。僅需小額付款(本文寫作時為0.01比特幣或大約5美元),Eugenia可以將7個字元的模式虛擬地址搜尋外套件,並在幾個小時內獲得結果,而不必進行幾個月的CPU搜尋。

產生虛擬地址是一個暴力搜尋:嘗試一個隨機金鑰,檢查結果地址以檢視它是否與所需模式匹配,重複直到成功。Vanity address miner 顯示了一個“虛榮礦工”的例子,這是一個用C++編寫的用於查詢虛榮地址的程式。這個例子使用了我們在[alt_libraries]中介紹的libbitcoin函式庫。

Example 9. Vanity address miner
link:code/vanity-miner.cpp[role=include]
Note

Compiling and running the vanity-miner example 使用 std::random_device. 根據具體實現不同,它可能反映了底層作業系統提供的CSRNG。在類別Unix等作業系統的情況下,它從+/dev/urandom+中提取。這裡使用的隨機數產生器用於示範目的,它不適合產生生產環境品質要求的比特幣金鑰,因為它沒有足夠的安全性來實現。

範例程式碼必須使用 C ++ 編譯器編譯並連結libbitcoin函式庫(必須先安裝在該系統上)。要執行該範例,請執行不帶引數的 vanity-miner 可執行檔案(參見Compiling and running the vanity-miner example),它將嘗試查詢以“1kid”開頭的虛擬地址。

Example 10. Compiling and running the vanity-miner example
$ # Compile the code with g++
$ g++ -o vanity-miner vanity-miner.cpp $(pkg-config --cflags --libs libbitcoin)
$ # Run the example
$ ./vanity-miner
Found vanity address! 1KiDzkG4MxmovZryZRj8tK81oQRhbZ46YT
Secret: 57cc268a05f83a23ac9d930bc8565bac4e277055f4794cbd1a39e5e71c038f3f
$ # Run it again for a different result
$ ./vanity-miner
Found vanity address! 1Kidxr3wsmMzzouwXibKfwTYs5Pau8TUFn
Secret: 7f65bbbbe6d8caae74a0c6a0d2d7b5c6663d71b60337299a1a2cf34c04b2a623
# Use "time" to see how long it takes to find a result
$ time ./vanity-miner
Found vanity address! 1KidPWhKgGRQWD5PP5TAnGfDyfWp5yceXM
Secret: 2a802e7a53d8aa237cd059377b616d2bfcfa4b0140bc85fa008f2d3d4b225349

real	0m8.868s
user	0m8.828s
sys	0m0.035s

我們可以看到,我們使用Unix命令 time 來測量執行時間,範例程式碼需要幾秒鐘找到三字元模式“kid”的匹配項。更改原始碼中的 search 模式並檢視四或五個字元模式需要多長時間!

虛榮地址的安全性

虛榮地址可以用來增強和破壞安全性,它們確實是一把雙刃劍。作為提高安全性時,獨特的地址使得攻擊者難以用自己的地址替代你的地址,並欺騙客戶付錢給他們,而不是你。不幸的是,虛榮地址也使得任何人都可以建立一個地址,以便將任何隨機地址或甚至另一個虛榮地址重新排列,從而欺騙客戶。

Eugenia 可以發佈一個隨機產生的地址(例如 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy ),人們可以向這個址傳送給他們的捐款。或者,她可以產生一個以1Kids開頭的虛榮地址,以使其更具特色。

在這兩種情況下,使用單個固定地址(而不是為每個捐助者單獨產生動態地址)的風險之一是小偷可能滲透你的網站並用自己的地址替換它,從而將捐贈轉移給自己。如果你在多個不同的地方刊登了捐款地址,使用者可能會在進行付款之前直觀地檢查地址,以確保它與你的網站,電子郵件和傳單上看到的地址相同。像 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy 這樣的隨機地址,普通使用者可能會檢查前幾個字元“1J7mdg”並確認地址匹配。使用虛名地址產生器,想竊取資金的人可以快速產生與前幾個字元匹配的地址,如Generating vanity addresses to match a random address所示。

Table 8. Generating vanity addresses to match a random address

Original Random Address

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

Vanity (4-character match)

1J7md1QqU4LpctBetHS2ZoyLV5d6dShhEy

Vanity (5-character match)

1J7mdgYqyNd4ya3UEcq31Q7sqRMXw2XZ6n

Vanity (6-character match)

1J7mdg5WxGENmwyJP9xuGhG5KRzu99BBCX

那麼虛榮的地址是否能增加安全性呢?如果Eugenia產生虛擬地址 1Kids33q44erFfpeXrmDSz7zEqG2FesZEN ,使用者可能會檢視虛空模式單詞 和後續的 的幾個字元,例如注意到地址的“1Kids33”部分。這會迫使攻擊者產生一個至少匹配六個角色(兩個以上)的虛榮地址,花費的努力比 Eugenia 花費4個字元虛榮心的努力高出3364倍(582)。從根本上說,Eugenia花費的努力(或支付虛榮礦工池)“推動”攻擊者必須產生更長的模式虛榮。如果Eugenia支付一個虛榮礦工池產生一個8個字元的虛榮地址,攻擊者將被推入10個角色的領域,這在個人計算機上是不可行的,即使使用訂製的虛榮採礦裝備或虛榮池也很昂貴。對於Eugenia而言,負擔得起的東西對於攻擊者來說是不可承受的,特別是如果潛在的欺詐收益不足以支付虛榮地址產生的代價。

紙錢包

紙錢包是印在紙上的比特幣私鑰。通常為方便起見,紙錢包還包括相應的比特幣地址,但這不是必須的,因為它可以用私鑰產生。紙錢包是建立備份或離線比特幣儲存(也稱為“冷儲存”)的非常有效的方式。作為備份機制,紙錢包可以防止由於計算機故障(如硬碟驅動器故障,被盜或意外刪除)而導致金鑰遺失。作為一種“冷儲存”機制,如果紙錢包金鑰是離線產生的,永遠不會儲存在計算機系統中,可以很好的防範黑客,按鍵記錄器和其他線上計算機威脅。

紙錢包可以有許多形狀,大小和設計,最基本的只是紙上的金鑰和地址。Simplest form of a paper wallet—a printout of the bitcoin address and private key 展示了紙錢包最簡單的形式。

Table 9. Simplest form of a paper wallet—a printout of the bitcoin address and private key
Public address Private key (WIF)

1424C2F4bC9JidNjjTUZCbUxv6Sa1Mt62x

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

例如位於 bitaddress.org 的客戶端JavaScript產生器的工具可以輕鬆產生紙錢包。此頁面包含產生金鑰和紙錢包所需的全部程式碼,即使與網際網路完全斷開。要使用它,請將HTML頁面儲存在本地硬碟或外部USB儲存上,斷開網際網路並在瀏覽器中開啟它。更好的是,使用乾淨的作業系統啟動計算機,例如可以從CD-ROM啟動的Linux作業系統。離線時使用此工具產生的任何金鑰都可以透過USB(非無線)在本地印表機上列印,從而建立紙錢包,其金鑰僅存在於紙張上,從未儲存在任何線上系統上。將這些紙錢包放入防火保險櫃中,並將比特幣“傳送”至其比特幣地址,以實施簡單而高效的“冷儲存”解決方案。An example of a simple paper wallet from bitaddress.org 展示了從bitaddress.org網站產生的紙錢包.

mbc2 0408
Figure 8. An example of a simple paper wallet from bitaddress.org

簡單紙錢包的缺點是印刷的鑰匙容易被盜。能夠訪問紙張的小偷可以竊取它或拍攝鑰匙,即可控制這些鑰匙鎖定的比特幣。更複雜的紙錢包儲存系統使用BIP-38加密的私人金鑰。紙錢包上印有的鑰匙受到由主人記在腦中的密碼保護。沒有密碼,加密的金鑰就沒用了。紙錢包仍然優於密碼保護的錢包,因為金鑰從未線上並且必須從安全或其他有物理保護的儲存裝置中獲取。An example of an encrypted paper wallet from bitaddress.org. The passphrase is "test." 顯示在bitaddress.org網站上建立的帶有加密私鑰(BIP-38)的紙幣。

mbc2 0409
Figure 9. An example of an encrypted paper wallet from bitaddress.org. The passphrase is "test."
Warning

儘管你可以多次將資金存入紙錢包,但你應該一次性收回所有資金,一次性花費。這是因為在解鎖和花費資金的過程中,如果花費少於全部金額,某些錢包可能會產生零錢地址。此外,如果你用來簽署交易的計算機受到威脅,可能會洩露私鑰。一次性花費紙錢包的全部餘額,可以降低金鑰洩漏的風險。如果你只需要少量資金,請在一筆交易中將剩餘資金髮送到一個新的紙錢包中。

紙錢包可以有許多不同的設計和尺寸,和不同的特徵。一些用來當作禮物贈送,並具有季節性主題,如聖誕節和新年主題。其他設計用於存放在銀行保險函式庫或帶有隱藏的密碼保護的保險箱中,或者使用不透明的刮擦貼紙,或者使用防篡改貼上箔摺疊和密封。圖 #paper_wallet_bpw#paper_wallet_spw 顯示具有安全和備份功能的各種紙錢包範例。

mbc2 0410
Figure 10. An example of a paper wallet from bitcoinpaperwallet.com with the private key on a folding flap
mbc2 0411
Figure 11. The bitcoinpaperwallet.com paper wallet with the private key concealed

其他設計還提供了鑰匙和地址的附加副本,形式為與票根類似的可拆卸存根,允許你儲存多個副本以防止火災,洪水或其他自然災害。

mbc2 0412
Figure 12. An example of a paper wallet with additional copies of the keys on a backup "stub"