什么是私鑰?
一般我們看到的私鑰是這樣的一段字符串:5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss
支持比特幣協議的應用都可以正確把這段字符串轉換成比特幣的私鑰,再轉換出公鑰,再得到一個地址,如果該地址上面有對應的比特幣,就可以使用這個私鑰花費上面的比特幣。
私鑰本質上是隨機數私鑰本質上是一個隨機數,由32個byte組成的數組,1個byte等于8位二進制,一個二進制只有兩個值0或者1。所以私鑰的總數是將近2^(8*32)=2^256個,但是有一些私鑰并不能使用,他真實的大小是介于:1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141之間的數。這個數量已經超過了宇宙中原子的總數,想要遍歷所有的私鑰,耗盡整個太陽的能量也是不可能的。
我們所說的比特幣私鑰的是密碼學上面安全的,并不是說不可能出現重復的私鑰,而是說不可能通過遍歷的方式找到某一個特定的私鑰,或者通過其它的方式找,而不通過私鑰就能花費地址上面的比特幣,私鑰的安全性是由數學上保證的。
私鑰的總數量很大,但是私鑰的生成是依賴隨機數的,真正的隨機是很難做到的,大部分私鑰的生成都是依賴于偽隨機算法(PRNG)。
偽隨機是用函數生成隨機數。它并不真正是隨機的。只是一個比較近似真隨機的隨機數。
私鑰生成的隨機性就很重要的,密碼學上安全的隨機是指:
隨機是不可預測的,隨機的結果是不可遍歷的,如果不是安全的隨機數生成器,生成的私鑰就有可能被別人碰撞到。不依賴隨機生成的私鑰就會大大的降低其生成的概率空間。
什么是公鑰?
公鑰是由數字和字母組成的另一個地址,這些數字和字母是通過使用數學函數加密后從私鑰派生出來的。加密過程是不可逆轉的,因此沒有人能夠找到原始的私鑰。這個地址可以讓你接收比特幣。
公鑰的哈希值總是1,它看起來是這樣的:1 bvbmseystwetqtfn5au4m4gfg7xjanvn2
這個地址您可以公開提供,以便接收比特幣。用戶可以生成的公共地址數量沒有限制。為了生成這樣的密鑰并隨后生成錢包地址,必須對私鑰進行多次轉換。這些轉換稱為哈希函數,是不可逆的轉換。
使用ECDSA創建公鑰
你要做的第一件事就是將ECDSA應用到你的私鑰上,也就是橢圓曲線數字簽名算法。定義的一個橢圓曲線方程為y2= x3+ ax + b, a和b為選定值。比特幣利用的是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’)
在上面給出的代碼中,使用編程器對私鑰進行解碼。在Python中,至少有兩個類可以保存私鑰和公鑰:“str”、字符串數組和“bytes”——字節數組,事情可能會變得有點混亂。
這是因為X字符串數組不等于X字節數組,但它等于有兩個元素的字節數組O《。codecs.decode方法將字符串轉換成字節數組。
在應用ECDSA之后,我們必須將字節0x04(04作為前綴)添加到生成的公鑰中。這將生成一個完整的比特幣公鑰。
壓縮公鑰
我們可以將公鑰壓縮得更短,而不是使用公鑰的長版本。
這是通過從ECDSA公鑰中獲取X并在Y的最后一個字節是偶數時添加0x02,如果最后一個字節是奇數,則添加0x03字節。
使用SHA-256和RIPEMD-160加密密鑰
現在我們繼續創建錢包地址。不管應用于公鑰的方法是什么,過程都是相同的。顯然,您將得到不同的結果地址。
為此,我們需要應用兩個哈希函數: 首先,我們將SHA-256應用于公鑰,然后使用RIPEMD-160加密結果。非常重要的是,算法應用的順序要準確。
在這個過程的最后,您將得到一個160位整數,它表示加密的公鑰。
下面是在Python中加密公鑰所需的代碼:
public_key_bytes = codecs.decode(public_key, ‘hex’)
# Run SHA-256 for the public key
sha256_bpk = hashlib.sha256(public_key_bytes)
sha256_bpk_digest = sha256_bpk.digest()
# Run RIPEMD-160 for the SHA-256
ripemd160_bpk = hashlib.new(‘ripemd160’)
ripemd160_bpk.update(sha256_bpk_digest)
ripemd160_bpk_digest = ripemd160_bpk.digest()
ripemd160_bpk_hex = codecs.encode(ripemd160_bpk_digest, ‘hex’)
添加網絡字節
由于比特幣有兩個網絡,主網和測試網,我們需要創建一個地址在主網使用。這意味著我們必須向加密的公鑰中添加0x00字節。對于測試網的使用,您必須添加0x6f字節。
計算校驗和
下一步是計算得到的主網密鑰的校驗和。校驗和確保密鑰在整個過程中仍然保持其完整性。如果校驗和不匹配,地址將被標記為無效。
為了生成密鑰的校驗和,必須應用SHA-256哈希函數兩次,然后從這個結果中取前4個字節。請記住,4個字節代表8個十六進制數字。
計算校驗和所需的代碼是:
# Double SHA256 to get checksum
sha256_nbpk = hashlib.sha256(network_bitcoin_public_key_bytes)
sha256_nbpk_digest = sha256_nbpk.digest()
sha256_2_nbpk = hashlib.sha256(sha256_nbpk_digest)
sha256_2_nbpk_digest = sha256_2_nbpk.digest()
sha256_2_hex = codecs.encode(sha256_2_nbpk_digest, ‘hex’)
checksum = sha256_2_hex[:8]
創建地址所需的最后一步是合并主網密鑰和校驗和。
用Base58編碼密鑰
您將注意到,生成的密鑰看起來不像其他BTC地址。這是因為大多數將它們轉換為Base58地址。
下面是將十六進制地址轉換為Base58地址所需的算法:
ef base58(address_hex):
alphabet = ‘123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz’
b58_string = ‘’
# Get the number of leading zeros
leading_zeros = len(address_hex) — len(address_hex.lstrip(‘0’))
# Convert hex to decimal
address_int = int(address_hex, 16)
# Append digits to the start of string
while address_int 》 0:
digit = address_int % 58
digit_char = alphabet[digit]
b58_string = digit_char + b58_string
address_int //= 58
# Add ‘1’ for each 2 leading zeros
ones = leading_zeros // 2
for one in range(ones):
b58_string = ‘1’ + b58_string
return b58_string
結果字符串將代表壓縮的比特幣錢包地址。
結論
如果您密切關注上述步驟,那么從私鑰生成比特幣錢包地址的過程并不困難。如果您的私鑰已滿或已壓縮,即使生成的地址將看起來不同,但它們都是有效的。
評論
查看更多