在加密貨幣中,私鑰允許用戶(hù)訪(fǎng)問(wèn)其錢(qián)包。持有私鑰的人完全控制該錢(qián)包中數(shù)字貨幣。出于這個(gè)原因,你應(yīng)該保守秘密。如果你真的想自己生成密鑰,那么以安全的方式生成密鑰是有意義的。在這里,我將介紹私鑰,并向你展示如何使用各種加密函數(shù)生成自己的密鑰。我將在Python中提供算法和代碼的描述。
我需要生成私鑰嗎?
大多數(shù)時(shí)候你沒(méi)有。例如,如果你使用Coinbase或Blockchain.info等網(wǎng)絡(luò)錢(qián)包,他們會(huì)為你創(chuàng)建和管理私鑰。交易所也是如此。
移動(dòng)和桌面錢(qián)包通常也會(huì)為你生成私鑰,但他們可以選擇使用你自己的私鑰創(chuàng)建錢(qián)包。
那么為什么要生成呢?以下是我的原因:
- 你想確保沒(méi)有人知道密鑰。
- 你只想了解有關(guān)加密和隨機(jī)數(shù)生成(RNG)的更多信息。
在此推薦小編創(chuàng)建的Python學(xué)習(xí)交流群:835017344,這里是python學(xué)習(xí)者聚集地,有大牛答疑,有資源共享!有想學(xué)習(xí)python編程的,或是轉(zhuǎn)行,或是大學(xué)生,還有工作中想提升自己能力的,正在學(xué)習(xí)的小伙伴歡迎加入學(xué)習(xí)。
什么是私鑰?
形式上,比特幣(以及許多其他加密貨幣)的私鑰是一系列32字節(jié)。現(xiàn)在,有很多方法可以記錄這些字節(jié)。它可以是256個(gè)1和0(32*8=256)或100個(gè)骰子所組成的字符串。它可以是二進(jìn)制字符串,Base64字符串,WIF密鑰,助記符短語(yǔ),或最后是十六進(jìn)制字符串。出于我們的目的,我們將使用64個(gè)字符長(zhǎng)的十六進(jìn)制字符串。
為什么正好是32字節(jié)?好問(wèn)題!你可以看到,要從私有密鑰創(chuàng)建公鑰,比特幣使用
ECDSA
或橢圓曲線(xiàn)數(shù)字簽名算法。更具體地說(shuō),它使用一個(gè)稱(chēng)為
secp256k1
的特定曲線(xiàn)。
現(xiàn)在,該曲線(xiàn)具有256位的量級(jí),以256位作為輸入,并輸出256位整數(shù)。256位正好是32個(gè)字節(jié)。因此,換句話(huà)說(shuō),我們需要32字節(jié)的數(shù)據(jù)來(lái)提供給這種曲線(xiàn)算法。
私鑰還有一個(gè)額外的要求。因?yàn)槲覀兪褂肊CDSA,所以關(guān)鍵應(yīng)該是正數(shù),并且應(yīng)該小于曲線(xiàn)的順序。secp256k1的順序是
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
,它非常大:幾乎任何32字節(jié)的數(shù)字都會(huì)比它小。
簡(jiǎn)單粗暴的方法
那么,我們?nèi)绾紊梢粋€(gè)32字節(jié)的整數(shù)? 首先想到的是只使用你選擇的語(yǔ)言的RNG庫(kù)。Python甚至提供了一種生成足夠位的友好方式:
import random
bits = random.getrandbits(256)
30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex(bits)
0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex[2:]
4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
看起來(lái)很好,但實(shí)際上,它不是。你看,普通的RNG庫(kù)不適用于加密,因?yàn)樗鼈儾皇呛馨踩K鼈兏鶕?jù)種子生成數(shù)字,默認(rèn)情況下,種子是當(dāng)前時(shí)間。這樣,如果你大致知道我上面生成的那些位,你需要做的就是暴力破解一些變種。
生成私鑰時(shí),你希望非常安全。請(qǐng)記住,如果有人學(xué)習(xí)私鑰,他們可以輕松地從相應(yīng)的錢(qián)包中竊取所有硬幣,而你沒(méi)有機(jī)會(huì)將其取回。
所以讓我們嘗試更安全地做到這一點(diǎn)。
密碼學(xué)上強(qiáng)大的RNG
除了標(biāo)準(zhǔn)的RNG方法,編程語(yǔ)言通常還提供專(zhuān)門(mén)用于加密操作的RNG。這種方法通常更加安全,因?yàn)樗苯訌牟僮飨到y(tǒng)中提取熵。這種RNG的結(jié)果很難再現(xiàn)。你不能通過(guò)知道生成時(shí)間或種子來(lái)做到這一點(diǎn),因?yàn)闆](méi)有種子。好吧,至少用戶(hù)不輸入種子;相反,它是由程序創(chuàng)建的。
在Python中,密碼強(qiáng)的RNG在
secrets
模塊中實(shí)現(xiàn)。讓我們修改上面的代碼,使私鑰生成安全!
import secrets
bits = secrets.randbits(256)
46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex(bits)
0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex[2:]
66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
這是驚人的。我敢打賭,即使訪(fǎng)問(wèn)我的電腦,你也無(wú)法重現(xiàn)這一點(diǎn)。但我們可以更深入了嗎?
專(zhuān)業(yè)的網(wǎng)站
有些網(wǎng)站會(huì)為你生成隨機(jī)數(shù)。我們?cè)谶@里只考慮兩個(gè)。一個(gè)是random.org ,一個(gè)眾所周知的通用隨機(jī)數(shù)發(fā)生器。另一個(gè)是bitaddress.org ,專(zhuān)門(mén)用于比特幣私鑰生成。
random.org可以幫助我們生成密鑰嗎? 當(dāng)然,因?yàn)樗麄冇猩呻S機(jī)字節(jié)的服務(wù) 。但是這里出現(xiàn)了兩個(gè)問(wèn)題。Random.org聲稱(chēng)是一個(gè)真正的隨機(jī)發(fā)生器,但你能相信嗎? 你能確定它確實(shí)是隨機(jī)的嗎? 你能確定所有者不記錄所有代的結(jié)果,特別是那些看起來(lái)像私鑰的結(jié)果嗎? 答案取決于你。哦,你不能在本地運(yùn)行它,這是一個(gè)額外的問(wèn)題。此方法不是100%安全。
現(xiàn)在,bitaddress.org是一個(gè)完全不同的故事。它是開(kāi)源的,所以你可以看到它的內(nèi)部代碼。它是客戶(hù)端,因此即使沒(méi)有Internet連接,你也可以下載并在本地運(yùn)行它。
那么它是怎樣工作的? 它使用你——是的,你自己——作為熵的來(lái)源。它會(huì)要求你移動(dòng)鼠標(biāo)或按隨機(jī)鍵。你做得足夠長(zhǎng),使得重現(xiàn)結(jié)果變得不可行。
你是否有興趣了解bitaddress.org的工作原理?出于學(xué)習(xí)目的,我們將查看其代碼并嘗試在Python中重現(xiàn)它。
快速說(shuō)明:bitaddress.org為你提供壓縮WIF格式的私鑰,該格式接近我們之前討論過(guò)的WIF格式。出于我們的目的,我們將使算法返回一個(gè)十六進(jìn)制字符串,以便我們以后可以使用它來(lái)生成公鑰。
Bitaddress:具體細(xì)節(jié)
Bitaddress以?xún)煞N形式創(chuàng)建熵:通過(guò)鼠標(biāo)移動(dòng)和按鍵壓力。我們將討論兩者,但我們將重點(diǎn)關(guān)注按鍵,因?yàn)楹茈y在Python lib中實(shí)現(xiàn)鼠標(biāo)跟蹤。我們希望最終用戶(hù)在我們有足夠的熵之前鍵入按鈕,然后我們將生成一個(gè)密鑰。
Bitaddress做了三件事。它初始化字節(jié)數(shù)組,試圖從你的計(jì)算機(jī)獲得盡可能多的熵,它用用戶(hù)輸入填充數(shù)組,然后生成一個(gè)私鑰。
Bitaddress使用256字節(jié)數(shù)組來(lái)存儲(chǔ)熵。這個(gè)數(shù)組是循環(huán)重寫(xiě)的,所以當(dāng)?shù)谝淮翁畛鋽?shù)組時(shí),指針變?yōu)榱悖⑶姨畛溥^(guò)程再次開(kāi)始。
程序從window.crypto啟動(dòng)一個(gè)256字節(jié)的數(shù)組。然后,它寫(xiě)入時(shí)間戳以獲得額外的4個(gè)字節(jié)的熵。最后,它獲取的數(shù)據(jù)包括屏幕大小,時(shí)區(qū),瀏覽器插件信息,區(qū)域設(shè)置等。這給了它另外6個(gè)字節(jié)。
初始化之后,程序不斷等待用戶(hù)輸入以重寫(xiě)初始字節(jié)。當(dāng)用戶(hù)移動(dòng)光標(biāo)時(shí),程序會(huì)寫(xiě)入光標(biāo)的位置。當(dāng)用戶(hù)按下按鈕時(shí),程序會(huì)寫(xiě)入按下的按鈕的字符代碼。
最后,bitaddress使用累積的熵來(lái)生成私鑰。它需要生成32個(gè)字節(jié)。對(duì)于此任務(wù),bitaddress使用名為ARC4的RNG算法。程序用當(dāng)前時(shí)間初始化ARC4并收集熵,然后逐個(gè)獲取32次字節(jié)。
這完全是對(duì)程序運(yùn)作方式的過(guò)度簡(jiǎn)化,但我希望你能得到這個(gè)想法。你可以在 Github 上詳細(xì)查看算法。
自己動(dòng)手吧
為了我們的目的,我們將構(gòu)建一個(gè)更簡(jiǎn)單的bitaddress版本。首先,我們不會(huì)收集有關(guān)用戶(hù)機(jī)器和位置的數(shù)據(jù)。其次,我們將僅通過(guò)文本輸入熵,因?yàn)槭褂肞ython腳本持續(xù)接收鼠標(biāo)位置非常具有挑戰(zhàn)性(如果你想這樣做,請(qǐng)檢查 PyAutoGUI )。
這將我們帶到了我們的生成器庫(kù)的正式規(guī)范。首先,它將使用加密RNG初始化一個(gè)字節(jié)數(shù)組,然后它將填充時(shí)間戳,最后它將填充用戶(hù)創(chuàng)建的字符串。種子池填滿(mǎn)后,庫(kù)將讓開(kāi)發(fā)人員創(chuàng)建一個(gè)密鑰。實(shí)際上,他們將能夠創(chuàng)建任意數(shù)量的私鑰,所有私鑰都由收集的熵保護(hù)。
初始化池
這里我們從加密RNG和時(shí)間戳中放入一些字節(jié)。
__seed_int
和
__seed_byte
是兩個(gè)將熵插入池?cái)?shù)組的輔助方法。請(qǐng)注意,我們使用
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
輸入種子
這里我們首先放置一個(gè)時(shí)間戳,然后輸入字符串。
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)
生成私鑰
這部分可能看起來(lái)很難,但實(shí)際上非常簡(jiǎn)單。
首先,我們需要使用我們的池生成32字節(jié)的數(shù)字。不幸的是,我們不能只創(chuàng)建自己的
random
對(duì)象,只能用于密鑰生成。相反,有一個(gè)共享對(duì)象,由在一個(gè)腳本中運(yùn)行的任何代碼使用。
這對(duì)我們意味著什么?
這意味著在每個(gè)時(shí)刻,代碼中的任何地方,一個(gè)簡(jiǎn)單的
random.seed(0)
都可以破壞我們收集的所有熵。我們不希望這樣。值得慶幸的是,Python提供了
getstate
和
setstate
方法。因此,為了在每次生成密鑰時(shí)保存我們的熵,我們記住我們停下來(lái)的狀態(tài),并在下次我們想要?jiǎng)?chuàng)建密鑰時(shí)設(shè)置它。
其次,我們只確保我們的鍵在范圍內(nèi)(1,
CURVE_ORDER
)。這是所有ECDSA私鑰的要求。
CURVE_ORDER
是secp256k1曲線(xiàn)的順序,它是
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
。
最后,為方便起見(jiàn),我們轉(zhuǎn)換為十六進(jìn)制,并剝離
'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
行動(dòng)
我們?cè)囍褂眠@個(gè)庫(kù)。實(shí)際上,它非常簡(jiǎn)單:你可以使用三行代碼生成私鑰!
kg = KeyGenerator()
kg.seed_input(‘Truly random string. I rolled a dice and got 4.’)
kg.generate_key()
60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2
你可以自己看看。密鑰是隨機(jī)的,完全有效。而且,每次運(yùn)行此代碼時(shí),都會(huì)得到不同的結(jié)果。
結(jié)論
如你所見(jiàn),有很多方法可以生成私鑰。它們的簡(jiǎn)單性和安全性不同。
生成私鑰只是第一步。下一步是提取可用于接收付款的公鑰和錢(qián)包地址。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

