如何用比特幣私鑰建立以太坊地址

買賣虛擬貨幣
區塊鏈的錢包中,私鑰可以產生出公鑰,而反過來要想從公鑰推算出私鑰則是不可能的。用公鑰加密的資訊可以用私鑰來解密,而用私鑰簽名的資訊則由公鑰來驗證,驗證透過後才能證明該資訊確實為私鑰持有人所釋出。以BTC為例的話,在這個過程中最重要的角色的就是“橢圓曲線加密演算法”。

有些人會以為BTC跟ETH是不同的鏈所以用的橢圓曲線並不相同,但事實上兩個鏈使用的都是相同的secp256k1曲線,所以獲得公鑰的方式完全一樣,差別在從公鑰生成地址的過程,接下來我們會先介紹如何安全的生成私鑰,然後說明ETH如何從地址驗證由私鑰生成的公鑰。


Vitalik曾經在Ethereum Community Forum上回復過為何不使用其他曲線
以下整理自 Timur Badretdinov 在 freeCodeCamp 上的一系列文章。
私鑰的規格secp256k1曲線上點的個數約有2²⁵⁶個,每個點可由一組256位代表,而256位正好是32個位元組,所以我們需要提供這個曲線演算法32個位元組的資料。並且因為我們使用ECDSA,金鑰必須是正數且小於該曲線。換句話說,BTC及ETH的私鑰都是一組32位元組的字串,但它也可以是二進位制字串、Base64字串、WIF金鑰、助記碼(mnemonic phrase)、十六進位制字串。



相同的私鑰,以不同的格式編寫。
安全的私鑰生成既然都知道他們使用的是同一條曲線,那我們其實就可以使用BTC社群比較信任的bitaddress.org來生成我們的私鑰,(用MEW或Metamask也都是不錯的選擇,至少他可以不是一串裸露在外的私鑰),但如果有良好安全意識的話,我們甚至不應該用瀏覽器來生成我們重要的私鑰,所以我們將用python設計一個更簡單的bitaddress。
bitaddress.org提供了WIF格式的私鑰
瞭解Bitaddress原理
Bitaddress做了三件事情。首先,初始化位元組陣列,然後嘗試從使用者的電腦獲得儘可能多的熵,根據使用者的輸入填滿陣列,最後生成私鑰。Bitaddress使用256位元組的陣列來儲存熵。這個陣列是被迴圈複寫的,所以當陣列第一次填滿時,索引變為零,然後複寫過程再次開始。程式從window.crypto生成一個256位元組的陣列。然後寫入一個時間戳來獲得4個位元組的熵。在這之後,它獲得一些其他的資料包括螢幕大小,時區,瀏覽器擴充套件,地區等。來獲得另外6個位元組。初始化後,使用者持續輸入來複寫初始位元組。當移動游標時,程式會寫入游標的位置。當按下按鈕時,程式會寫入按下的按鈕的字元程式碼。最後,bitaddress使用累積的熵來生成私鑰。bitaddress使用名為ARC4的RNG演算法。用當前時間以及收集的熵初始化ARC4,然後逐個取得位元組,總共取32次。初始化我們自己的種子池
我們從加密RNG和時間戳中寫入一些位元組。__seed_int以及__seed_byte是將熵插入池的陣列中的兩個函式,而我們使用secrets生成我們的隨機數。
def __init_pool(self):
for i in range(self.POOL_SIZE):
random_byte = secrets.randbits(8)
self.__seed_byte(random_byte)
time_int = int(time.time())
self.__seed_int(time_int)
def __seed_int(self, n):
self.__seed_byte(n)
self.__seed_byte(n >> 8)
self.__seed_byte(n >> 16)
self.__seed_byte(n >> 24)
def __seed_byte(self, n):
self.pool[self.pool_pointer] ^= n & 255
self.pool_pointer += 1
if self.pool_pointer >= self.POOL_SIZE:
self.pool_pointer = 0


由輸入填充種子池

這裡我們先寫入一個時間戳,然後寫入使用者輸入的字串。

def seed_input(self, str_input):
    time_int = int(time.time())
    self.__seed_int(time_int)
    for char in str_input:
        char_code = ord(char)
        self.__seed_byte(char_code)
生成私鑰
首先使用我們的池生成32位的數字,並確保我們的私鑰在範圍內(1,CURVE_ORDER),然後為了方便,我們轉為十六進位制並刪除0x的部分。
def generate_key(self):
big_int = self.__generate_big_int()
big_int = big_int % (self.CURVE_ORDER — 1) # key < curve order
big_int = big_int + 1 # key > 0
key = hex(big_int)[2:]
return key
def __generate_big_int(self):
if self.prng_state is None:
seed = int.from_bytes(self.pool, byteorder='big', signed=False)
random.seed(seed)
self.prng_state = random.getstate()
random.setstate(self.prng_state)
big_int = random.getrandbits(self.KEY_BYTES * 8)
self.prng_state = random.getstate()
return big_int
最後僅需三行就可以生成我們的私鑰。
kg = KeyGenerator()
kg.seed_input(‘Truly random string. I rolled a dice and got 4.’)
kg.generate_key()

生成ETH公鑰

將我們剛剛的私鑰代入橢圓曲線,我們會得到一個64位元組的整數,它是兩個32位元組的整數,代表橢圓曲線上連線在一起的X點和Y點。

private_key_bytes = codecs.decode(private_key, 'hex')
# 獲得 ECDSA 公鑰
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')
錢包地址要從公鑰建立地址時,我們只需要將公鑰帶入Keccak-256(你可能會聽到一些人稱呼他為“卡咖256”),然後獲得回傳值的最後20個位元組。沒有Base58或任何其他轉換,唯一需要的是在地址的開頭新增0x。
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:]
校驗和(ERC-55)
比特幣透過將公鑰雜湊後並獲得回傳值的前4個位元組來建立校驗和,如果不新增校驗和則無法獲得有效地址。但以太坊一開始並沒有校驗和機制來驗證公鑰的完整性。直到Vitalik Buterin在2016年時引入了校驗和機制,也就是EIP-55,並且後來被各家錢包和交易所採用。將校驗和新增到以太坊錢包地址使其區分大小寫首先,獲得地址的Keccak-256雜湊值。需要注意的是,將此地址傳遞至雜湊函式時不能有0x的部分。其次,依序迭代初始地址的位元組。如果雜湊值的第i個位元組大於或等於8,則將第i個地址的字元轉換為大寫,否則將其保留為小寫。最後,在回傳的字串開頭加回0x。如果忽略大小寫,校驗和地址會與初始地址相同。但使用大寫字母的地址讓任何人都能檢驗地址是否有效。
此校驗和有幾個好處:1.向後相容許多接受混合大小寫的十六進位制解析器,將來也能輕鬆引入2. 保持長度為40個字元3.平均每個地址將有15個校驗位,如果輸入錯誤,隨機生成的地址意外透過檢查的淨概率將為0.0247%,雖然不如4位元組的校驗程式碼好,但比ICAP提高了約50倍
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)
結論
為以太坊建立錢包地址相較於比特幣簡單得多。我們需要做的就只是將私鑰丟到橢圓曲線,然後再把得到的公鑰丟到Keccak-256,最後擷取該雜湊值的後面20個位元組。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;