在加密貨幣中,私鑰允許用戶訪問其錢包。持有私鑰的人完全控制該錢包中數字貨幣。出于這個原因,你應該保守秘密。如果你真的想自己生成密鑰,那么以安全的方式生成密鑰是有意義的。在這里,我將介紹私鑰,并向你展示如何使用各種加密函數生成自己的密鑰。我將在Python中提供算法和代碼的描述。
我需要生成私鑰嗎?
大多數時候你沒有。例如,如果你使用Coinbase或Blockchain.info等網絡錢包,他們會為你創建和管理私鑰。交易所也是如此。
移動和桌面錢包通常也會為你生成私鑰,但他們可以選擇使用你自己的私鑰創建錢包。
那么為什么要生成呢?以下是我的原因:
- 你想確保沒有人知道密鑰。
- 你只想了解有關加密和隨機數生成(RNG)的更多信息。
在此推薦小編創建的Python學習交流群:835017344,這里是python學習者聚集地,有大牛答疑,有資源共享!有想學習python編程的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小伙伴歡迎加入學習。
什么是私鑰?
形式上,比特幣(以及許多其他加密貨幣)的私鑰是一系列32字節。現在,有很多方法可以記錄這些字節。它可以是256個1和0(32*8=256)或100個骰子所組成的字符串。它可以是二進制字符串,Base64字符串,WIF密鑰,助記符短語,或最后是十六進制字符串。出于我們的目的,我們將使用64個字符長的十六進制字符串。
為什么正好是32字節?好問題!你可以看到,要從私有密鑰創建公鑰,比特幣使用
ECDSA
或橢圓曲線數字簽名算法。更具體地說,它使用一個稱為
secp256k1
的特定曲線。
現在,該曲線具有256位的量級,以256位作為輸入,并輸出256位整數。256位正好是32個字節。因此,換句話說,我們需要32字節的數據來提供給這種曲線算法。
私鑰還有一個額外的要求。因為我們使用ECDSA,所以關鍵應該是正數,并且應該小于曲線的順序。secp256k1的順序是
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
,它非常大:幾乎任何32字節的數字都會比它小。
簡單粗暴的方法
那么,我們如何生成一個32字節的整數? 首先想到的是只使用你選擇的語言的RNG庫。Python甚至提供了一種生成足夠位的友好方式:
import random
bits = random.getrandbits(256)
30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex(bits)
0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex[2:]
4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
看起來很好,但實際上,它不是。你看,普通的RNG庫不適用于加密,因為它們不是很安全。它們根據種子生成數字,默認情況下,種子是當前時間。這樣,如果你大致知道我上面生成的那些位,你需要做的就是暴力破解一些變種。
生成私鑰時,你希望非常安全。請記住,如果有人學習私鑰,他們可以輕松地從相應的錢包中竊取所有硬幣,而你沒有機會將其取回。
所以讓我們嘗試更安全地做到這一點。
密碼學上強大的RNG
除了標準的RNG方法,編程語言通常還提供專門用于加密操作的RNG。這種方法通常更加安全,因為它直接從操作系統中提取熵。這種RNG的結果很難再現。你不能通過知道生成時間或種子來做到這一點,因為沒有種子。好吧,至少用戶不輸入種子;相反,它是由程序創建的。
在Python中,密碼強的RNG在
secrets
模塊中實現。讓我們修改上面的代碼,使私鑰生成安全!
import secrets
bits = secrets.randbits(256)
46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex(bits)
0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex[2:]
66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
這是驚人的。我敢打賭,即使訪問我的電腦,你也無法重現這一點。但我們可以更深入了嗎?
專業的網站
有些網站會為你生成隨機數。我們在這里只考慮兩個。一個是random.org ,一個眾所周知的通用隨機數發生器。另一個是bitaddress.org ,專門用于比特幣私鑰生成。
random.org可以幫助我們生成密鑰嗎? 當然,因為他們有生成隨機字節的服務 。但是這里出現了兩個問題。Random.org聲稱是一個真正的隨機發生器,但你能相信嗎? 你能確定它確實是隨機的嗎? 你能確定所有者不記錄所有代的結果,特別是那些看起來像私鑰的結果嗎? 答案取決于你。哦,你不能在本地運行它,這是一個額外的問題。此方法不是100%安全。
現在,bitaddress.org是一個完全不同的故事。它是開源的,所以你可以看到它的內部代碼。它是客戶端,因此即使沒有Internet連接,你也可以下載并在本地運行它。
那么它是怎樣工作的? 它使用你——是的,你自己——作為熵的來源。它會要求你移動鼠標或按隨機鍵。你做得足夠長,使得重現結果變得不可行。
你是否有興趣了解bitaddress.org的工作原理?出于學習目的,我們將查看其代碼并嘗試在Python中重現它。
快速說明:bitaddress.org為你提供壓縮WIF格式的私鑰,該格式接近我們之前討論過的WIF格式。出于我們的目的,我們將使算法返回一個十六進制字符串,以便我們以后可以使用它來生成公鑰。
Bitaddress:具體細節
Bitaddress以兩種形式創建熵:通過鼠標移動和按鍵壓力。我們將討論兩者,但我們將重點關注按鍵,因為很難在Python lib中實現鼠標跟蹤。我們希望最終用戶在我們有足夠的熵之前鍵入按鈕,然后我們將生成一個密鑰。
Bitaddress做了三件事。它初始化字節數組,試圖從你的計算機獲得盡可能多的熵,它用用戶輸入填充數組,然后生成一個私鑰。
Bitaddress使用256字節數組來存儲熵。這個數組是循環重寫的,所以當第一次填充數組時,指針變為零,并且填充過程再次開始。
程序從window.crypto啟動一個256字節的數組。然后,它寫入時間戳以獲得額外的4個字節的熵。最后,它獲取的數據包括屏幕大小,時區,瀏覽器插件信息,區域設置等。這給了它另外6個字節。
初始化之后,程序不斷等待用戶輸入以重寫初始字節。當用戶移動光標時,程序會寫入光標的位置。當用戶按下按鈕時,程序會寫入按下的按鈕的字符代碼。
最后,bitaddress使用累積的熵來生成私鑰。它需要生成32個字節。對于此任務,bitaddress使用名為ARC4的RNG算法。程序用當前時間初始化ARC4并收集熵,然后逐個獲取32次字節。
這完全是對程序運作方式的過度簡化,但我希望你能得到這個想法。你可以在 Github 上詳細查看算法。
自己動手吧
為了我們的目的,我們將構建一個更簡單的bitaddress版本。首先,我們不會收集有關用戶機器和位置的數據。其次,我們將僅通過文本輸入熵,因為使用Python腳本持續接收鼠標位置非常具有挑戰性(如果你想這樣做,請檢查 PyAutoGUI )。
這將我們帶到了我們的生成器庫的正式規范。首先,它將使用加密RNG初始化一個字節數組,然后它將填充時間戳,最后它將填充用戶創建的字符串。種子池填滿后,庫將讓開發人員創建一個密鑰。實際上,他們將能夠創建任意數量的私鑰,所有私鑰都由收集的熵保護。
初始化池
這里我們從加密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字節的數字。不幸的是,我們不能只創建自己的
random
對象,只能用于密鑰生成。相反,有一個共享對象,由在一個腳本中運行的任何代碼使用。
這對我們意味著什么?
這意味著在每個時刻,代碼中的任何地方,一個簡單的
random.seed(0)
都可以破壞我們收集的所有熵。我們不希望這樣。值得慶幸的是,Python提供了
getstate
和
setstate
方法。因此,為了在每次生成密鑰時保存我們的熵,我們記住我們停下來的狀態,并在下次我們想要創建密鑰時設置它。
其次,我們只確保我們的鍵在范圍內(1,
CURVE_ORDER
)。這是所有ECDSA私鑰的要求。
CURVE_ORDER
是secp256k1曲線的順序,它是
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
。
最后,為方便起見,我們轉換為十六進制,并剝離
'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()
60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2
你可以自己看看。密鑰是隨機的,完全有效。而且,每次運行此代碼時,都會得到不同的結果。
結論
如你所見,有很多方法可以生成私鑰。它們的簡單性和安全性不同。
生成私鑰只是第一步。下一步是提取可用于接收付款的公鑰和錢包地址。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
