密鑰和地址

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

簡介

比特幣的所有權通過 數字密鑰(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的私鑰始終會生成相同的公鑰KkK 之間的關係是固定的,但只能從 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用於在錢包之間匯入/匯出,並經常用於私鑰的QR碼(條形碼)表示。

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",將僅用於生成壓縮的公鑰(請參閱壓縮的公鑰)。

公鑰格式

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

如之前所見,公鑰是由一對座標(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座標,但我們必須儲存ysign(正數或負數);換句話說,我們必須記住它高於或低於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[]

該程式碼使用預定義的私鑰在每次運行時產生相同的比特幣地址,如 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[]

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[]

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"開頭。 The range of vanity addresses starting with "1Kids" 顯示了具有前綴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[]
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.png
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.png
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.png
Figure 10. An example of a paper wallet from bitcoinpaperwallet.com with the private key on a folding flap
mbc2 0411.png
Figure 11. The bitcoinpaperwallet.com paper wallet with the private key concealed

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

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