科普 | 如何通過私鑰建立以太坊錢包地址?
編者注:我們翻譯和出版於與密碼學貨幣的私鑰、地址和錢包有關的內容,無非希望傳達清楚幾個要點:1. 密碼學貨幣不同於傳統的銀行,你有很多工具可以生成一把私鑰來持有密碼學貨幣,既不需要向銀行申請,也不需要給誰報備,沒有任何人能阻止你擁有自己的私鑰和錢包;2. 公鑰和用來接收轉賬的地址都是由私鑰使用單向的數學運算推匯出來的,如果不信任現有的工具,你完全可以自己使用這些數學運算來生成地址;同時,公開地址不會產生安全問題,因為地址無法反推出公鑰,也無法反推出私鑰;3. 使用第三方提供的服務時,弄清楚服務的性質,不要向任何人暴露自己的私鑰,並且定期備份。
在本系列文章的 第一篇 中,我們得到了如下的比特幣私鑰:
60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2
編者注:這裡沒有寫出私鑰的生成過程。簡單來說,私鑰就是一串隨機的十六進位制字串,為了安全(私鑰不暴露、不被他人重現出來),這串隨機數的生成環境應儘可能滿足隨機性、不可預測性、不可重現性。所以,不要自己寫一串數字來當私鑰,因為你自以為的“隨機”往往並不怎麼隨機,很不安全。(理論上來說你確實可以自己連拋 256 次硬幣產生符合長度要求(64 位)的隨機數,但還是很不推薦。)
在本文中,我們會演示使用這個私鑰來獲得公開地址,以及與該私鑰對應的以太坊錢包地址。
通過私鑰來獲得比特幣錢包地址的具體流程有些複雜,因此我們會描述簡化後的版本。我們需要使用一個雜湊函式去獲得公鑰,還需要使用另一個函式去獲得地址。
現在,讓我們開始吧。
公鑰
這部分內容和之前 討論比特幣的文章 中所說的相同,所以如果你已經讀完了,那麼就可以跳過(除非你想要複習一下)。
首先,我們需要在私鑰上使用 ECDSA,即橢圓曲線數字簽名演算法。橢圓曲線是通過 y² = x³ + ax + b 公式得出的,其中 a 和 b 可以自定義。橢圓曲線家族有很多知名並且廣泛應用的案例。比特幣使用了 secp256k1 曲線,關於橢圓曲線密碼學,如果你想了解更多,可以參考 此文章 。
以太坊使用了同樣的橢圓曲線,secp256k1,因此對於比特幣和以太坊來說,獲得公鑰的流程是相同的。
對私鑰作了 ECDSA 運算之後,我們得到了 64 位元組的整數,這是由兩個 32 位元組的整數串聯組成,代表了橢圓曲線上某個點的 X 值和 Y 值。
在 Python 程式中,程式碼顯示如下:
private_key_bytes = codecs.decode(private_key, ‘hex’) # Get ECDSA public key key = ecdsa.SigningKey.from_string(private_key_bytes, curve=ecdsa.SECP256k1).verifying_key key_bytes = key.to_string() key_hex = codecs.encode(key_bytes, ‘hex’)
注意:從上面的程式碼可以看出,我使用了 ecdsa 模組並通過編碼器解碼了私鑰。這樣寫更多是因為 Python 的關係,而與演算法本身無關,為免誤解,讓我來好好解釋一下。
Python 語言中,至少有兩種資料型別可以儲存私鑰和公鑰:“str”和“bytes”。前者對應的是 string(字串),後者則是 byte array(數值)。Python 語言中的密碼學運算只能對“bytes”類操作,將 byte 型資料作為輸入,並且將輸出作為結果。
但是,這裡面有個小問題:作為字串的“4f3c”和作為 byte array 的 4f3c
是不等同的,string 等於 byte array 和兩個元素 O<
的結合。codecs.decode 方法就是將字串轉換為 byte array。本文中使用的密碼學操作都要進行這一步驟。
錢包地址
一旦獲得公鑰,我們就可以計算出錢包地址,和比特幣不同,以太坊在主網和所有測試網都有相同的地址。當用戶發起轉賬和簽名的時候,他們需要選擇相應的網路。
為了通過公鑰得出地址,我們需要做的就是在公鑰上應用 Keccak-256 加密演算法,然後拿出結果的後 20 個位元組,這樣就可以了。整個過程不需要其他的雜湊函式,無需 Base58 編碼,也不用其他任何轉換,你唯一需要做的事情就是在地址的開頭新增“0x”。
Python程式碼如下:
public_key_bytes = codecs.decode(public_key, ‘hex’) keccak_hash = keccak.new(digest_bits=256) keccak_hash.update(public_key_bytes) keccak_digest = keccak_hash.hexdigest() # Take the last 20 bytes wallet_len = 40 wallet = ‘0x’ + keccak_digest[-wallet_len:]
校驗和(checksum)
Now, as you may remember, Bitcoin creates the checksum by hashing the public key and taking the first 4 bytes of the result. This is true for all Bitcoin addresses, so you can’t get the valid address without adding the checksum bytes.
我們都知道,比特幣是對公鑰使用雜湊演算法,然後取結果的前 4 個數字,以此建立校驗和。這對於所有比特幣地址來說都是適用的,因此在沒有新增 checksum 位元組之前,使用者無法獲得有效地址。
編者注:校驗和(checksum)是一種較為簡單的驗證資料完整性的方法,具體方法有很多種,比如說對一段資料逐次取 4 個位元,把取出的數全部加起來,最後得到一個 4 個位元的值作為校驗和。如果兩段資料不一樣,產生的校驗和有極大概率是不一樣的。跟雜湊函式的原理有相似之處,但夠不上密碼學雜湊函式那樣的強度。)
例:
MD5(cvsoiu687y0adbfiq7et5tgho0) = a277a316d38c21786eac518b83af898f
MD5(wysoiu687y0adbfiq7et5tgho0) = becd314fb8d277cfe20aaadc2b52c177
在以太坊中,產生地址的流程與此並不相同。最初的時候,以太坊中沒有校驗和這樣的機制來驗證祕鑰的完整性。但是在 2016 年,Vitalik Buterin 引進了 checksum 機制,現在已經被錢包提供商和交易所使用。
在以太坊錢包地址上新增 checksum 使得我們可以通過大小寫來校驗地址的有效性。
首先,你需要獲得地址的 Keccak-256 雜湊值。注意,將地址放入雜湊函式的時候不可以新增 0x 部分。
其次,你需要迭代初始地址的字元,如果雜湊值中的第 i 個位元組(byte)大於或者等於 8,那麼你要將地址中的第 i 個字元變為大寫,否則就還是保持小寫。
最後,你需要把 0x 新增到結果的開頭。如果忽略大小寫,那麼校驗和地址與初始地址是相同的。但是,這種使用大寫字母的做法讓人們可以隨時隨地檢查地址是否有效。你可以通過 這個網頁 找到有效驗證 checksum 的演算法。
通過 checksum 驗證方法,我們可以得到下面的結論:
“平均來看每個地址有 15 個校驗位,並且隨機得出的錯誤地址能夠偶然通過檢驗的概率為 0.0247%。”
下面是將 checksum 新增到以太坊地址的程式碼:
checksum = ‘0x’ # Remove ‘0x’ from the address address = address[2:] address_byte_array = address.encode(‘utf-8’) keccak_hash = keccak.new(digest_bits=256) keccak_hash.update(address_byte_array) keccak_digest = keccak_hash.hexdigest() for i in range(len(address)): address_char = address[i] keccak_char = keccak_digest[i] if int(keccak_char, 16) >= 8: checksum += address_char.upper() else: checksum += str(address_char)
結論
如文中所述,和比特幣相比,建立以太坊地址要容易地多。我們需要做的事情就是用私鑰在 ECDSA 上找出公鑰,然後使用 Keccak-256 演算法,並以最終雜湊值的後 20 個位元組作為地址。
如果你想使用這些程式碼,我已經把它們釋出到這個 GitHub repository 上了.
編者注:如上圖所示,以太坊的公鑰和地址都由私鑰生成,並且其生成所需的數學運算都是完全公開的。
作者:Timur Badretdinov
翻譯&校對:Eric Wang & 阿劍