16进制数字与wif格式私钥互转
from base58 import b58encode, b58decode
from binascii import hexlify, unhexlify
from hashlib import sha256
import unittest
def encode_private_key_as_wif(private_key):
"""
秘密鍵 (16 進数文字列) を WIF 形式に変換する。
"""
# 先頭の 0x80 は秘密鍵をエンコードする際の version byte を表す。
key = '80' + private_key
tmp = key
# SHA-256 関数を 2 回適用する。
tmp = sha256(unhexlify(tmp)).hexdigest()
tmp = sha256(unhexlify(tmp)).hexdigest()
# 末尾の 8 バイトをチェックサムとする。
checksum = tmp[0:8]
wif_private_key = b58encode(unhexlify(key + checksum))
return wif_private_key
def decode_wif_private_key(wif_private_key):
"""
秘密鍵 (WIF 形式) を 16 進数文字列に変換する。
"""
tmp = hexlify(b58decode(wif_private_key)).decode('utf-8')
# version byte とチェックサムを取り除く。
private_key = tmp[2:-8]
# b58decode の代わりに b58decode_check を使う場合
# tmp = hexlify(b58decode_check(wif_private_key)).decode('utf-8')
# private_key = tmp[2:]
return private_key
def verify_checksum(wif_private_key):
"""
秘密鍵のチェックサムを検証する。
"""
b58decoded = hexlify(b58decode(wif_private_key)).decode('utf-8')
tmp = b58decoded[:-8]
tmp = sha256(unhexlify(tmp)).hexdigest()
tmp = sha256(unhexlify(tmp)).hexdigest()
return b58decoded[-8:] == tmp[:8]
class WIFTest(unittest.TestCase):
"""
WIF 形式に関する関数の動作を検証するための単体テスト。
検証のための値は https://en.bitcoin.it/wiki/Wallet_import_format から借用している。
"""
def test_encode_private_key_as_wif(self):
private_key = '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
wif_private_key = encode_private_key_as_wif(private_key)
self.assertEqual('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ', wif_private_key)
def test_decode_wif_private_key(self):
wif_private_key = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
private_key = decode_wif_private_key(wif_private_key)
self.assertEqual('0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d', private_key)
def test_verify_checksum(self):
valid_wif_private_key = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
invalid_wif_private_key = 'SHueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
self.assertTrue(verify_checksum(valid_wif_private_key))
self.assertFalse(verify_checksum(invalid_wif_private_key))
if __name__ == '__main__':
unittest.main()
私钥转比特币地址
import bitcoin
msgs = [
'所有的「压缩」,都表示公钥坐标转换为公钥值时的压缩',
'压缩密钥不是把密钥压缩,而是指仅用来生成压缩公钥的密钥',
'压缩地址也不是把地址压缩,而是用压缩公钥生成的地址'
]
print('='*80)
print('\n'.join(m.center(60, ' ') for m in msgs))
print('='*80)
# 生成一个随机的密钥
while True:
# 生成一个用十六进制表示的长 256 位的私钥(str类型)
private_key = bitcoin.random_key()
# 解码为十进制的整形密钥
decoded_private_key = bitcoin.decode_privkey(private_key, 'hex')
if 0 < decoded_private_key < bitcoin.N:
break
print(f'密钥(十六进制):{private_key} (长 256 位)')
print(f'密钥(十进制):{decoded_private_key} (0 到 1.158*10**77 之间)')
# 用 WIF 格式编码密钥
wif_encoded_private_key = bitcoin.encode_privkey(decoded_private_key, 'wif')
print(f'密钥(WIF):{wif_encoded_private_key} (5 开头,长 51 字符)')
# 用 01 标识的压缩密钥
compressed_private_key = private_key + '01'
print(f'压缩密钥(十六进制):{compressed_private_key} (01 结尾,长 264 位)')
# 生成 WIF的压缩格式
wif_compressed_private_key = bitcoin.encode_privkey(
bitcoin.decode_privkey(compressed_private_key, 'hex'), 'wif')
print(f'压缩密钥(WIF):{wif_compressed_private_key} (L/K 开头)')
# 计算公钥坐标 K = k * G
public_key = bitcoin.fast_multiply(bitcoin.G, decoded_private_key)
print(f'公钥(坐标):{public_key}')
# 转十六也可用 bitcoin.encode(xxx, 16)
print(f'公钥(坐标的十六进制):{tuple(hex(i) for i in public_key)}')
# 计算公钥
hex_encoded_public_key = bitcoin.encode_pubkey(public_key, 'hex')
print(f'公钥(十六进制):{hex_encoded_public_key} (04 x y)')
# 计算压缩公钥
# if public_key[1] % 2 == 0: # 两种方式均可
if public_key[1] & 1 == 0:
compressed_prefix = '02'
else:
compressed_prefix = '03'
# 转十六也可用 bitcoin.encode(xxx, 16)
hex_compressed_public_key = compressed_prefix + hex(public_key[0])[2:]
print(f'压缩公钥(十六进制){hex_compressed_public_key} '
'(02 开头代表 y 是偶数,03 开头代表 y 是奇数)')
# 计算地址
# 传入公钥坐标对象/十六进制公钥值,输出同样的地址
# 传入压缩公钥值,输出与⬆️不同的地址
print(f'地址(b58check):{bitcoin.pubkey_to_address(public_key)} (1 开头)')
print(type(hex_compressed_public_key))
print('压缩地址(b58check):'
f'{bitcoin.pubkey_to_address(hex_compressed_public_key)} (1 开头)')
以太坊地址生成过程如下
1、生成 256 位随机数作为私钥。
2、将私钥转化为 secp256k1 非压缩格式的公钥,即 512 位的公钥。
3、使用散列算法 Keccak256 计算公钥的哈希值,转化为十六进制字符串。
4、取十六进制字符串的后 40 个字母,开头加上 0x 作为地址。
#!/usr/bin/env python3
# pip install ecdsa
# pip install pysha3
from ecdsa import SigningKey, SECP256k1
import sha3
def checksum_encode(addr_str): # Takes a hex (string) address as input
keccak = sha3.keccak_256()
out = ''
addr = addr_str.lower().replace('0x', '')
keccak.update(addr.encode('ascii'))
hash_addr = keccak.hexdigest()
for i, c in enumerate(addr):
if int(hash_addr[i], 16) >= 8:
out += c.upper()
else:
out += c
return '0x' + out
keccak = sha3.keccak_256()
priv = SigningKey.generate(curve=SECP256k1)
pub = priv.get_verifying_key().to_string()
keccak.update(pub)
address = keccak.hexdigest()[24:]
def test(addrstr):
assert (addrstr == checksum_encode(addrstr))
test('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed')
test('0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359')
test('0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB')
test('0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb')
test('0x7aA3a964CC5B0a76550F549FC30923e5c14EDA84')
print("Private key:", priv.to_string().hex())
print("Public key: ", pub.hex())
print("Address: ", checksum_encode(address))