比特幣加密算法的python實踐

比特幣加密算法的python實踐

就像我們在銀行的賬戶密碼一樣,決定比特幣一個賬戶所有權的是與賬戶相關的三個核心概念:私鑰、公鑰和比特幣地址。比特幣錢包和交易與它們息息相關。

(一) 私鑰、公鑰和錢包地址(比特幣地址)之間的關係

比特幣加密算法的python實踐

每個比特幣錢包中包含一系列的密鑰對,每個密鑰對包括一個私鑰和一個公鑰。私鑰(k)是一個數字,通常是隨機選出的。有了私鑰,我們就可以使用橢圓曲線乘法這個單向加密函數產生一個公鑰(K)。有了公鑰(K),我們就可以使用一個單向加密哈希函數生成比特幣地址(A)。

事實上,比特幣地址可以看作一種公鑰。

比特幣加密算法的python實踐

他們之間的關係如上圖所示,私鑰可以通過橢圓加密算法生成公鑰,公鑰可以通過哈希函數生成比特幣地址。反之,無法從比特幣地址推斷生成公鑰,也無法從公鑰推斷生成私鑰,這是由後面要介紹的非對稱加密算法所實現的。

值得一提的是,掌握私鑰就能生成相應的公鑰和比特幣地址(錢包地址),相當於掌握了整個賬戶,所以我們一定要保管好自己的私鑰。

(二)私鑰、公鑰和錢包地址(比特幣地址)生成流程圖:

比特幣加密算法的python實踐

1. 首先使用隨機數發生器生成一個『私鑰』。一般來說這是一個256bits的數,擁有了這串數字就可以對相應『錢包地址』中的比特幣進行操作,所以必須被安全地保存起來。

2. 『私鑰』經過SECP256K1算法處理生成了『公鑰』。SECP256K1是一種橢圓曲線算法,通過一個已知『私鑰』時可以算得『公鑰』,而『公鑰』已知時卻無法反向計算出『私鑰』。這是保障比特幣安全的算法基礎。

3. 同SHA256一樣,RIPEMD160也是一種Hash算法,由『公鑰』可以計算得到『公鑰哈希』,而反過來是行不通的。

4. 將一個字節的地址版本號連接到『公鑰哈希』頭部(對於比特幣網絡的pubkey地址,這一字節為“0”),然後對其進行兩次SHA256運算,將結果的前4字節作為『公鑰哈希』的校驗值,連接在其尾部。

5. 將上一步結果使用BASE58進行編碼(比特幣定製版本),就得到了『錢包地址』。

比如, 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

(三)加密算法在比特幣錢包和交易中的應用場景。

『私鑰』用來生成『公鑰』和『錢包地址』,也用來對交易進行簽名。擁有了『私鑰』就是擁有了對這個錢包餘額的一切操作權力。

(1)使用『私鑰』對交易進行簽名

比特幣錢包間的轉賬是通過交易(Transaction)實現的。交易數據是由轉出錢包『私鑰』的所有者生成,也就是說有了『私鑰』就可以花費該錢包的比特幣餘額。生成交易的過程如下:

比特幣加密算法的python實踐

1. 交易的原始數據包括“轉賬數額”和“轉入錢包地址”,但是僅有這些是不夠的,因為無法證明交易的生成者對“轉出錢包地址”餘額有動用的權利。所以需要用『私鑰』對原始數據進行簽名。

2. 生成“轉出錢包公鑰”,這一過程與生成『錢包地址』的第2步是一樣的。

3. 將“轉出簽名”和“轉出公鑰”添加到原始交易數據中,生成了正式的交易數據,這樣它就可以被廣播到比特幣網絡進行轉賬了。

2)使用『公鑰』對簽名進行驗證

比特幣加密算法的python實踐

交易數據被廣播到比特幣網絡後,節點會對這個交易數據進行檢驗,其中就包括對簽名的校驗。如果校驗正確,那麼這筆餘額就成功地從“轉出錢包”轉移到“轉入錢包”了。

(四)生成私鑰、公鑰、比特幣地址的示例代碼。

橢圓加密算法使用的是 python 的 ecdsa 庫,哈希算法使用的是 hashlib 。 另外沒講到的部分是 base58Checkencode , 這是在表示比特幣地址時所用的方法,可以把地址壓縮得更短 ,使得表示更為清晰。

(1)首先需要通過終端安裝 ecdsa 包(橢圓加密算法庫)。

Linux系統:sudo pip install ecdsa

Windows系統:直接下載源碼安裝。

(2)Python代碼:(windows,python2.7 idle)

# -*- coding: UTF-8 -*-

import ecdsa

import ecdsa.der

import ecdsa.util

import hashlib

import os

import re

import struct

b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def base58encode(n):

result = ''

while n > 0:

result = b58[n%58] + result

n /= 58

return result

def base256decode(s):

result = 0

for c in s:

result = result * 256 + ord(c)

return result

def countLeadingChars(s, ch):

count = 0

for c in s:

if c == ch:

count += 1

else:

break

return count

def base58CheckEncode(version, payload):

s = chr(version) + payload

checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]

result = s + checksum

leadingZeros = countLeadingChars(result, '\0')

return '1' * leadingZeros + base58encode(base256decode(result))

def privateKeyToWif(key_hex):

return base58CheckEncode(0x80, key_hex.decode('hex'))

def privateKeyToPublicKey(s):

sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)

vk = sk.verifying_key

return ('\04' + sk.verifying_key.to_string()).encode('hex')

def pubKeyToAddr(s):

ripemd160 = hashlib.new('ripemd160')

ripemd160.update(hashlib.sha256(s.decode('hex')).digest())

return base58CheckEncode(0, ripemd160.digest())

def keyToAddr(s):

return pubKeyToAddr(privateKeyToPublicKey(s))

#Generate a random private key

private_key = os.urandom(32).encode('hex')

print "Secret Exponent (Uncompressed) : %s " % private_key

print "Public Key : %s" % privateKeyToPublicKey(private_key)

print "Private Key : %s " % privateKeyToWif(private_key)

print "Address : %s " % keyToAddr(private_key)

(3)輸出:

比特幣加密算法的python實踐


分享到:


相關文章: