这两天在学习STEEM的交易签名,由于智商感人,所以进展缓慢。里边涉及有一些公钥私钥方面的内容,然后惊觉我之前对这部分内容的了解,已经忘干净了!从头再学,有些压力,还好之前学习的内容都记录在STEEMIT上,把之前的相关帖子都翻看一下,顺便摘录一些重点贴在这篇文章中,算是做笔记吧。
STEEM中照搬了借鉴了比特币(Bitcoin)的那一套东西,所以之前写的比特币(Bitcoin)一系列文章对自己还是很有参考价值的。
本文主要涉及以下方面内容:
因为涉及私钥、公钥的时候经常要用到Base58 & Base58Check编解码,所以首先简要介绍一下这两个东西。
Base58简单地说,就是把数字表示成不容易输入错误的文本编码,因为这组不容易出错的文本编码一共包含58个字符,所以叫Base58
编解码规则也很简单,编码就是把文本表示数字串,转换成一个大整数,然后再按58进制表示。
58进制和16进制类似,16进制数字0-15和0x0 - 0xF是一一对应的,而这个0-57,需要查表对应,仅此而已!解码就是逆过程!
码表为:
BASE58_ALPHABET = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
Base58Check在就是Base58基础上加上了版本信息以及校验,这样如果不小心输错一两个字符,就会检查出来。
Base58Check流程可以用下图表示:
Image source Here
整理成文字说明如下:
私钥就是一个256位,取值处于1到n - 1之间的随机数
其中: n = 1.158 * 1077
私钥有几种表示方式
其中WIF亦即: Wallet Import Format (WIF)
前缀0x80, 并用Base58Check编码前缀0x80,并加上后缀0x01, 并用Base58Check编码私钥可以用hashlib.sha256来生成(仅供参考,请勿使用)
>>> import hashlib
>>> from binascii import hexlify, unhexlify
>>> s = hashlib.sha256(bytes('Hello World', 'utf-8')).digest()
>>> print(hexlify(s).decode('ascii'))
a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
涉及到椭圆曲线等数学知识我就方了,直接上干货吧。
公钥(K)可以通过椭圆曲线运算由私钥(k)计算得出私钥(k)到公钥(K)计算公式:K=k∗Gsecp256k1标准中定义的椭圆曲线以及一组数学常量私钥(k)到公钥(K)结果是确定的,并且只能单向运算私钥(k)到公钥(K)的计算K=k∗G其中小k是我们的私钥,G是生成点,K是结果亦即公钥是曲线上的另外一个点。
因为生成点对所有的比特币用户都是相同的,所以同一个私钥k乘以生成点G,总会得到相同的公钥K。所以从k到K是确定的,并且只能单向运算。这就是为何比特币地址(由公钥生成)可以告诉任何人不用担心泄露私钥。
公钥(K)是通过私钥(k)乘以生成点(G)得到的,并且是椭圆曲线上的一个点P(x, y)
将点P(x, y)表示成公钥其实就是加上前缀04并把x和y连接起来
一共是520bits (8 + 256 + 256),使用16进制字符串表示,要130个字节,浪费网络资源。
公钥压缩原理:
根据椭圆曲线的方程,我们是可以通过x求得y的,所以我们只要保留x部分就可以了
公钥压缩流程:
公钥压缩的示意图( Source: 《Mastering Bitcoin》)
import ecdsa
from binascii import hexlify, unhexlify
secret = unhexlify('a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e')
order = ecdsa.SigningKey.from_string(secret, curve=ecdsa.SECP256k1).curve.generator.order()
p = ecdsa.SigningKey.from_string(secret, curve=ecdsa.SECP256k1).verifying_key.pubkey.point
x_str = ecdsa.util.number_to_string(p.x(), order)
y_str = ecdsa.util.number_to_string(p.y(), order)
compressed = hexlify(bytes(chr(2 + (p.y() & 1)), 'ascii') + x_str).decode('ascii')
uncompressed = hexlify(bytes(chr(4), 'ascii') + x_str + y_str).decode('ascii')
p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
x = int(hexlify(x_str).decode('ascii'), 16)
y = int(hexlify(y_str).decode('ascii'), 16)
(x ** 3 + 7 - y**2) % p
比特币地址由公钥按固定流程生成。
生成流程如下:
根据地址生成流程可知:
地址和私钥本身是不压缩的,只是代表对应的公钥是压缩的而已
def ripemd160(s):
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(unhexlify(s))
return ripemd160.digest()
def get_address(public_key):
pkbin = unhexlify(public_key)
addressbin = ripemd160(hexlify(hashlib.sha256(pkbin).digest()))
address = base58CheckEncode(0x00, hexlify(addressbin).decode('ascii'))
return address
免责声明,本文为个人理解,示例仅供参考
因使用文中代码或地址造成的损失,概不负责!