温故而知新 /比特币(Bitcoin)有关的 Base58 & Base58Check、私钥(Private KEY)、公钥(Public KEY)、地址(Address)

in #cn7 years ago

这两天在学习STEEM的交易签名,由于智商感人,所以进展缓慢。里边涉及有一些公钥私钥方面的内容,然后惊觉我之前对这部分内容的了解,已经忘干净了!从头再学,有些压力,还好之前学习的内容都记录在STEEMIT上,把之前的相关帖子都翻看一下,顺便摘录一些重点贴在这篇文章中,算是做笔记吧。

STEEM中照搬了借鉴了比特币(Bitcoin)的那一套东西,所以之前写的比特币(Bitcoin)一系列文章对自己还是很有参考价值的。

本文主要涉及以下方面内容:

  • Base58 & Base58Check
  • 私钥(Private KEY)
  • 公钥(Public KEY)
  • 地址(Address)

Base58 & Base58Check

因为涉及私钥、公钥的时候经常要用到Base58 & Base58Check编解码,所以首先简要介绍一下这两个东西。

Base58

Base58简单地说,就是把数字表示成不容易输入错误的文本编码,因为这组不容易出错的文本编码一共包含58个字符,所以叫Base58

编解码规则也很简单,编码就是把文本表示数字串,转换成一个大整数,然后再按58进制表示。

58进制和16进制类似,16进制数字0-15和0x0 - 0xF是一一对应的,而这个0-57,需要查表对应,仅此而已!解码就是逆过程!

码表为:
BASE58_ALPHABET = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

Base58Check

Base58Check在就是Base58基础上加上了版本信息以及校验,这样如果不小心输错一两个字符,就会检查出来。

Base58Check流程可以用下图表示:

Image source Here

整理成文字说明如下:

  • 把版本/程序信息字节和负载(payload)按字节连接到一起
  • 对结果一执行两次SHA256操作并取前四个字节
  • 按字节把结果一和结果二的四个字节连接起来
  • 把结果三当成一个大数字,对结果三执行Bash58编码
    (原链接第四步,分为4、5、6三个步骤,包括如何处理前导字节零)

私钥(Private KEY)

私钥是什么

私钥就是一个256位,取值处于1到n - 1之间的随机数
其中: n = 1.158 * 1077

私钥表示方式

私钥有几种表示方式

其中WIF亦即: Wallet Import Format (WIF)

  • 64位16进制串就是把私钥直接转换
  • Base58编码就是对64位16进制串直接编码 (https://blockchain.info中有用到)
  • WIF就是在64位16进制串对应的字节串前加上前缀0x80, 并用Base58Check编码
  • WIF-compressed就是在64位16进制串对应的字节串前加上前缀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


公钥(Public KEY)

涉及到椭圆曲线等数学知识我就方了,直接上干货吧。

如何生成公钥

  • 公钥(K)可以通过椭圆曲线运算由私钥(k)计算得出
  • 私钥(k)公钥(K)计算公式:K=k∗G
  • 生成过程使用secp256k1标准中定义的椭圆曲线以及一组数学常量
  • 私钥(k)公钥(K)结果是确定的,并且只能单向运算
  • 使用Python的ecdsa库,可以轻松实现私钥(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


地址(Address)

比特币地址由公钥按固定流程生成。

生成流程如下:

根据地址生成流程可知:

  • 压缩公钥生成对应压缩公钥的地址
  • 未压缩公钥生成对应未压缩公钥的地址

地址和私钥本身是不压缩的,只是代表对应的公钥是压缩的而已

示例代码:公钥生成地址

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

参考资料(以前文章)

免责声明,本文为个人理解,示例仅供参考
因使用文中代码或地址造成的损失,概不负责!

Sort:  

讲的很详细的,但是发现看不懂的占绝大多数,我就放心了,😀

记得很久之前想知道 WIF 是啥意思,一直没找到,结果才这里学到了,哈哈。

我知道啥叫wtf

awesome but can`t understand ur lang

看完我周二的大脑袋开始晕眩。。。反正只要你自己不丢,就都是特别特别安全很难很难解开的密码就是了。。

有种在读 Andreas M. Antonopoulos 的《Mastering Bitcoin》的感觉。

图都是从《Mastering Bitcoin》拿过来的啊 😀
是本超级好书,可惜我只读了一点点

之前看了一阵就停了,太多数学的东西看得好辛苦,读了你的理解再看回去应该会明白更多。

Peace, mercy and blessings of God

i will always support you man!!

腦細胞剩一個了..... T_T

智商感人➕1……

虽然不太懂但感觉很cool

Good post!!Thank you:)

不错哦 椭圆曲线那部分说实话看了也看不懂 听了课 还是懵懵的

讲得挺详细,不过还没看太懂

沒想到你還真的有點料啊?我都帶泳褲來了,卻沒得游泳~~~~

那就把泳裤也脱了吧

很好的信息..投票给你

不明觉厉