比特幣開發專題(數字貨幣調用使用高級密鑰和地址)


區塊鏈愛好者(QQ:53016353) 

高級密鑰和地址

在以下部分中,我們將看到高級形式的密鑰和地址,諸如加密私鑰、腳本和多重簽名地址,靚號地址,和紙錢包。
4.5.1 加密私鑰(BIP0038)
私鑰必須保密。私鑰的機密性需求事實情況是,在實踐中相當難以實現,因為該需求與同樣重要的安全對象可用性相互矛盾。當你需要為了避免私鑰丟失而存儲備份時,會發現維護私鑰私密性是一件相當困難的事情。通過密碼加密內有私鑰的錢包可能要安全一點,但那個錢包也需要備份。有時,例如用戶因為要升級或重裝錢包軟件,而需要把密鑰從一個錢包轉移到另一個。私鑰備份也可能需要存儲在紙張上(參見“4.5.4 紙錢包”一節)或者外部存儲介質里,比如U盤。但如果一旦備份文件失竊或丟失呢?這些矛盾的安全目標推進了便攜、方便、可以被眾多不同錢包和數字貨幣客戶端理解的加密私鑰標准BIP0038的出台。
BIP0038提出了一個通用標准,使用一個口令加密私鑰並使用Base58Check對加密的私鑰進行編碼,這樣加密的私鑰就可以安全地保存在備份介質里,安全地在錢包間傳輸,保持密鑰在任何可能被暴露情況下的安全性。這個加密標准使用了AES,這個標准由NIST建立,並廣泛應用於商業和軍事應用的數據加密。
BIP0038加密方案是:輸入一個數字貨幣私鑰,通常使用WIF編碼過,base58chek字符串的前綴“5”。此外BIP0038加密方案需要一個長密碼作為口令,通常由多個單詞或一段復雜的數字字母字符串組成。BIP0038加密方案的結果是一個由base58check編碼過的加密私鑰,前綴為6P。如果你看到一個6P開頭的的密鑰,這就意味着該密鑰是被加密過,並需要一個口令來轉換(解碼)該密鑰回到可被用在任何錢包WIT格式的私鑰(前綴為5)。許多錢包APP現在能夠識別BIP0038加密過的私鑰,會要求用戶提供口令解碼並導入密鑰。第三方APP,諸如非常好用基於瀏覽器的Bit Address,可以被用來解碼BIP00038的密鑰。
最通常使用BIP0038加密的密鑰用例是紙錢包——一張紙張上備份私鑰。只要用戶選擇了強口令,使用BIP0038加密的私鑰紙錢包就無比的安全,是一種很棒的線下數字貨幣存儲途徑(也被稱作“冷庫”)。
在bitaddress.org上測試表4-10中加密密鑰,看看你如何得到輸入口令的加密密鑰。
表4-10 BIP0038加密私鑰例子
私鑰(WIF) 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
密碼 MyTestPassphrase
加密私鑰(BIP0038) 6PRTHL6mWa48xSopbU1cKrVjpKbBZxcLRRCdctLJ3z5yxE87MobKoXdTsJ
4.5.2 P2SH (Pay-to-Script Hash)和多重簽名地址
正如我們所知,傳統的數字貨幣地址從數字1開頭,來源於公鑰,而公鑰來源於私鑰。雖然任何人都可以將數字貨幣發送到一個1開頭的地址,但數字貨幣只能在通過相應的私鑰簽名和公鑰哈希值后才能消費。
以數字3開頭的數字貨幣地址是P2SH地址,有時被錯誤的稱謂多重簽名或多重簽名地址。他們指定數字貨幣交易中受益人作為哈希的腳本,而不是公鑰的所有者。這個特性在2012年1月由BIP0016引進,目前因為BIP0016提供了增加功能到地址本身的機會而被廣泛的采納。不同於發送資金到傳統1開頭的數字貨幣地址的交易,也被稱為P2PKH,資金被發送到3開頭的地址時,不僅僅需要一個公鑰的哈希值,同時也需要一個私鑰簽名作為所有者證明。在創建地址的時候,這些要求會被定義在腳本中,所有對地址的輸入都會被這些要求阻隔。
一個P2SH地址從事務腳本中創建,它定義誰能消耗這個事務輸出。(132頁“P2SH(Pay-to-Script-Hash)”一節對此有詳細的介紹)編碼一個P2SH地址涉及使用一個在創建數字貨幣地址用到過的雙重哈希函數,並且只能應用在腳本而不是公鑰:
script hash = RIPEMD160(SHA256(script))
腳本哈希的結果是由Base58Check編碼前綴為5的版本、編碼后得到開頭為3的編碼地址。一個P2SH地址例子是32M8ednmuyZ2zVbes4puqe44NZumgG92sM。
P2SH 不一定是多重簽名的交易。雖然P2SH地址通常都是代表多重簽名,但也可能是其他類型的交易腳本。
4.5.2.1 多重簽名地址和P2SH
目前,P2SH函數最常見的實現是用於多重簽名地址腳本。顧名思義,底層腳本需要多個簽名來證明所有權,此后才能消費資金。設計數字貨幣多重簽名特性是需要從總共N個密鑰中需要M個簽名(也被稱為“閾值”),被稱為M-的-N的多簽名,其中M是等於或小於N。例如,第一章中提到的咖啡店主鮑勃使用多重簽名地址需要1-2簽名,一個是屬於他的密鑰和一個屬於他同伴的密鑰,以確保其中一方可以簽署度過一個事務鎖定輸出到這個地址。這類似於傳統的銀行中的一個“聯合賬戶”,其中任何一方配偶可以憑借單一簽名消費。或Gopesh, Bob雇佣的網頁設計師創立一個網站,可能為他的業務需要一個2-3的多簽名地址,確保沒有資金會被花費除非至少兩個業務合作伙伴簽署這筆交易。
我們將會在第五章節探索如何使用P2SH地址創建事務用來消費資金。
4.5.3 數字貨幣靚號地址
靚號地址包含了可讀信息的有效數字貨幣地址。例如,1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33就是包含了Base-58字母love的。靚號地址需要生成並通過數十億的候選私鑰測試,直到一個私鑰能生成具有所需圖案的數字貨幣地址。雖然有一些優化過的靚號生成算法,該方法必須涉及隨機上選擇一個私鑰,生成公鑰,再生成數字貨幣地址,並檢查是否與所要的靚號圖案相匹配,重復數十億次,直到找到一個匹配。
一旦找到一個匹配所要圖案的靚號地址,來自這個靚號地址的私鑰可以和其他地址相同的方式被擁有者消費數字貨幣。靚號地址不比其他地址具有更多安全性。它們依靠和其他地址相同的ECC和SHA。你無法比任何別的地址更容易的獲得一個靚號圖案開頭的私鑰。
在第一章中,我們介紹了Eugenia,一位在菲律賓工作的兒童慈善總監。我們假設Eugenia組織了一場數字貨幣募捐活動,並希望使用靚號數字貨幣地址來宣布這個募捐活動。Eugenia將會創造一個以1Kids開頭的靚號地址來促進兒童慈善募捐的活動。讓我們看看這個靚號地址如何被創建,這個靚號地址對Eugenia慈善募捐的安全性又意味着什么。
4.5.3.1 生成靚號地址
我們必須認識到使用來自Base58字母表中簡單符號來代表數字貨幣地址是非常重要的。搜索“1kids”開頭的圖案我們會發現從1Kids11111111111111111111111111111到1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz的地址。這些以“1kid”開頭的地址范圍中大約有58的29次方地址。表4-11顯示了這些有“1kids”前綴的地址。
表4-11 “1Kids”靚號的范圍
From 1Kids11111111111111111111111111111
To 1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz
我們把“1Kids”這個前綴當作數字,我們可以看看數字貨幣地址中這個前綴出現的頻率。如果是一台普通性能的桌面電腦,沒有任何特殊的硬件,可以每秒發現大約10萬個密鑰。
表4-12 靚號的出現的頻率(1KidsCharity)以及生成所需時間
長度 地址前綴 概率 平均生成時間
1 1K 1/58 < 1毫秒
2 1Ki 1/3,364 50毫秒
3 1Kid 1/(195*103) < 2秒
4 1Kids 1/(11*106) 1分鍾
5 1KidsC 1/(656*106) 1小時
6 1KidsCh 1/(38*109) 2天
7 1KidsCha 1/(2.2*1012) 3–4 月
8 1KidsChar 1/(128*1012) 13–18年
9 1KidsChari 1/(7*1015) 800年
10 1KidsCharit 1/(400*1015) 46,000年
11 1KidsCharity 1/(23*1018) 250萬年
正如你所見,Eugenia將不會很快地創建出以“1KidsCharity”開頭的靚號地址,即使她有數千台的電腦同時進行運算。每增加一個字符就會增加58倍的計算難度。超過七個字符的搜索模式通常需要專用的硬件才能被找出,譬如用戶定制的具有多圖形處理單元(GPU)的桌面級設備。那些通常是無法繼續在數字貨幣挖礦中盈利的鑽機,被重新賦予了尋找靚號地址的任務。用GPU系統搜索靚號的速度比用通用CPU要快很多個量級。
另一種尋找靚號地址的方法是將工作外包給一個礦池里的靚號礦工們,如靚號礦池中的礦池。一個礦池是一種允許那些GPU硬件通過為他人尋找靚號地址來獲得數字貨幣的服務。對小額的賬單,Eugenia可以外包搜索模式為7個字符靚號地址尋找工作,在幾個小時內就可以得到結果,而不必用一個CPU搜索上幾個月才得到結果。
生成一個靚號地址是一項通過蠻力的過程:嘗試一個隨機密鑰,檢查結果地址是否和所需的圖案想匹配,重復這個過程直到成功找到為止。例4-8是個靚號礦工的例子,用C++程序來尋找靚號地址。這個例子運用到了我們在56頁“其他替代客戶端、資料庫、工具包”一節介紹過的libbitcoin庫。
例4-8 靚號挖掘程序
#include // The string we are searching forconst std::string search = "1kid";// Generate a random secret key. A random 32 bytes.bc::ec_secret random_secret(std::default_random_engine& engine); // Extract the Bitcoin address from an EC secret.std::string bitcoin_address(const bc::ec_secret& secret);// Case insensitive comparison with the search string.bool match_found(const std::string& address);int main() {    std::random_device random;     std::default_random_engine engine(random());     // Loop continuously...    while (true)     {        // Generate a random secret.        bc::ec_secret secret = random_secret(engine);         // Get the address.        std::string address = bitcoin_address(secret);         // Does it match our search string? (1kid)        if (match_found(address))         {            // Success!            std::cout << "Found vanity address! " << address << std::endl;             std::cout << "Secret: " << bc::encode_hex(secret) << std::endl; return 0;        }     }    // Should never reach here!    return 0; }bc::ec_secret random_secret(std::default_random_engine& engine){    // Create new secret...    bc::ec_secret secret;    // Iterate through every byte setting a random value... for (uint8_t& byte: secret)        byte = engine() % std::numeric_limits::max();     // Return result.    return secret;}std::string bitcoin_address(const bc::ec_secret& secret) {    // Convert secret to pubkey...    bc::ec_point pubkey = bc::secret_to_public_key(secret);     // Finally create address.    bc::payment_address payaddr; bc::set_public_key(payaddr, pubkey);    // Return encoded form.    return payaddr.encoded(); }bool match_found(const std::string& address) {    auto addr_it = address.begin();    // Loop through the search string comparing it to the lower case     // character of the supplied address.    for (auto it = search.begin(); it != search.end(); ++it, ++addr_it)        if (*it != std::tolower(*addr_it))             return false;    // Reached end of search string, so address matches.    return true;     }
示例程序需要用C編譯器鏈接libbitcoin庫(此庫需要提前裝入該系統)進行編譯。直接執行vanity-miner的可執行文件(不用參數,參見例4-9),它就會嘗試碰撞以“1kid”開頭的數字貨幣地址。
例4-9 編譯並運行vanity-miner程序示例
$ # Compile the code with g++$ g++ -o vanity-miner vanity-miner.cpp $(pkg-config --cflags --libs libbitcoin) $ # Run the example$ ./vanity-minerFound vanity address! 1KiDzkG4MxmovZryZRj8tK81oQRhbZ46YTSecret: 57cc268a05f83a23ac9d930bc8565bac4e277055f4794cbd1a39e5e71c038f3f$ # Run it again for a different result$ ./vanity-minerFound vanity address! 1Kidxr3wsmMzzouwXibKfwTYs5Pau8TUFnSecret: 7f65bbbbe6d8caae74a0c6a0d2d7b5c6663d71b60337299a1a2cf34c04b2a623# Use "time" to see how long it takes to find a result$ time ./vanity-minerFound vanity address! 1KidPWhKgGRQWD5PP5TAnGfDyfWp5yceXMSecret: 2a802e7a53d8aa237cd059377b616d2bfcfa4b0140bc85fa008f2d3d4b225349real    0m8.868suser    0m8.828ssys     0m0.035s
正如我們運行Unix命令time所測出的運行時間所示,示例代碼要花幾秒鍾來找出匹配“kid”三個字符模板的結果。讀者們可以在源代碼中改變search這一搜索模板,看一看如果是四個字符或者五個字符的搜索模板需要花多久時間!
4.5.3.2 靚號地址安全性
靚號地址既可以增加、也可以削弱安全措施,它們着實是一把雙刃劍。用於改善安全性時,一個獨特的地址使對手難以使用他們自己的地址替代你的地址,以欺騙你的顧客支付他們的賬單。不幸的是,靚號地址也可能使得任何人都能創建一個類似於隨機地址的地址,甚至另一個靚號地址,從而欺騙你的客戶。
Eugenia可以讓捐款人捐款到她宣布的一個隨機生成地址(例如:1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy)。或者她可以生成一個以“1Kids”開頭的靚號地址以顯得更獨特。
在這兩種情況下,使用單一固定地址(而不是每比捐款用一個獨立的動態地址)的風險之一是小偷有可能會黑進你的網站,用他自己的網址取代你的網址,從而將捐贈轉移給自己。如果你在不同的地方公布了你的捐款地址,你的用戶可以在付款之前直觀地檢查以確保這個地址跟在你的網站、郵件和傳單上看到的地址是同一個。在隨機地址1j7mdg5rbqyuhenydx39wvwk7fslpeoxzy的情況下,普通用戶可能會只檢查頭幾個字符“1j7mdg”,就認為地址匹配。使用靚號地址生成器,那些想通過替換類似地址來盜竊的人可以快速生成與前幾個字符相匹配的地址,如表4-13所示。
表4-13 生成匹配某隨機地址的多個靚號
原版隨機地址 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
4位字符匹配 1J7md1QqU4LpctBetHS2ZoyLV5d6dShhEy
5位字符匹配 1J7mdgYqyNd4ya3UEcq31Q7sqRMXw2XZ6n
6位字符匹配 1J7mdg5WxGENmwyJP9xuGhG5KRzu99BBCX
那靚號地址會不會增加安全性?如果Eugenia生成1Kids33q44erFfpeXrmDSz7zEqG2FesZEN的靚號地址,用戶可能看到靚號圖案的字母和一些字符在上面,例如在地址部分中注明了1Kids33。這樣就會迫使攻擊者生成至少6個字母相匹配的的靚號地址(比之前多2個字符),就要花費比Eugenia多3364倍的靚號圖案。本質上,Eugenia付出的努力(或者靚號池付出的)迫使攻擊者不得不生成更長的靚號圖案。如果Eugenia花錢請礦池生成8個字符的靚號地址,攻擊者將會被逼迫到10字符的境地,那將是個人電腦,甚至昂貴自定義靚號挖掘機或靚號池也無法生成。對Eugenia來說可承擔的起支出,對攻擊者來說則變成了無法承擔支出,特別是如果欺詐的回報不足以支付生成靚號地址所需的費用。
4.5.4 紙錢包
紙錢包是打印在紙張上的數字貨幣私鑰。有時紙錢包為了方面起見也包括對應的數字貨幣地址,但這並非是必要的。因為地址可以從私鑰中導出。紙錢包是一個非常有效簡歷備份或者線下數字貨幣存儲方式,也是被稱為“冷錢包”。作為備份機制,一個紙錢包可以提供安全性,以防在電腦硬盤損壞、失竊或意外刪除的情況下造成密鑰的的丟失。作為一個冷存儲的機制,如果紙錢包密鑰在線下生成並永久不在電腦系統中存儲,他們在應對黑客攻擊,鍵盤記錄器,或其他在線電腦欺騙更有安全性。
紙錢包有許多不同的形狀,大小,和外觀設計,但非常基本的原則是一個密鑰和一個地址打印在紙張上。表4-14展現了紙錢包最基本的形式。
表4-14 數字貨幣紙錢包的私鑰和公鑰的打印形式
公開地址 1424C2F4bC9JidNjjTUZCbUxv6Sa1Mt62x
私鑰(WIF) 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
通過使用工具,就可以很容易地生成紙錢包,譬如使用bitaddress.org網站上的客戶端Javascript生成器。這個頁面包含所有必要的代碼,甚至在完全失去網絡連接的情況下,也可以生成密鑰和紙錢包。若要使用它,先將HTML頁面保存在本地磁盤或外部U盤。從Internet網絡斷開,從瀏覽器中打開文件。更方便的,使用一個原始操作系統啟動電腦,比如一個光盤啟動的Linux系統。任何在脫機情況下使用這個工具所生成的密鑰,都可以通過USB線在本地打印機上打印出來,從而制造了密鑰只存在紙張上而從未存儲在在線系統上的紙錢包。將這些紙錢包放置在防火容器內,發送數字貨幣到對應的數字貨幣地址上,從而實現了一個簡單但非常有效的冷存儲解決方案。圖4-14展示了通過bitaddress.org 生成的紙錢包。


圖4-14 通過bitaddress.org 生成的普通紙錢包
這個簡單的紙錢包系統的不足之處是那些被打印下來的密鑰容易被盜竊。一個能夠獲取接近這些紙幣的小偷可以只需偷走紙幣或者用把紙幣上密鑰拍攝下來,就能獲得被這些密鑰加密過的數字貨幣的控制權。一個更復雜的紙錢包存儲系統使用BIP0038加密的私鑰。這些私鑰被打印在紙錢包上被所有者記住的口令保護起來。沒有口令,這些被加密過的密鑰也是毫無用處的。但它們仍舊要比用口令保護的錢包級別要高,因為這些密鑰從沒有在線過,必須從物理上從保險箱或者其他物理安全存儲中導出。圖4-15展示了通過bitaddress.org 生成的加密紙錢包。


圖4-15 通過bitaddress.org 生成的加密紙錢包。密碼是“test”。
雖然你可以多次存款到紙錢包中,但是你最好一次性提款,一次性提取里面所有的資金。因為如果你提取的金額少於其中的金額的話,會生成一個找零地址。並且,你所用的電腦可能被病毒感染,那么就有可能泄露私鑰。一次性提款可以減少私鑰泄露的風險,如果你所需的金額比較少,那么請把余額找零到另一個紙錢包中。
紙錢包有許多設計和大小,並有許多不同的特性。有些作為禮物送給他人,有季節性的主題,像聖誕節和新年主題。另外一些則是設計保存在銀行金庫或通過某種方式隱藏私鑰的保險箱內,或者用不透明的刮刮貼,或者折疊和防篡改的鋁箔膠粘密封。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com