比特币:账户私钥、公钥、地址的生成

时间:2024-02-29 15:30:26

由上一篇ECC算法笔记记录的公钥生成方式:K = k * G :

私钥 k 通常是随机选出的一串数字串(经过sha256加密),然后通过ECC算法来产生一个公钥(K),最后通过单项Hash算法来生成Bitcoin地址。

描述:

  • 地址是为了人们交换方便而弄出来的一个方案,因为公钥太长了(130字符串或66字符串)。地址长度为25字节,转为base58编码后,为34或35个字符。base58是类似base64的编码,但去掉了易引起视觉混淆的字符,又在地址末尾添加了4个字节校验位,保障在人们交换个别字符错误时,也能够因地址校验失败而制止了误操作。
  • 由于存在公钥有两种形式,那么一个公钥便对应两个地址。这两个地址都可由同一私钥签署交易。

密钥类型

存在三种密钥,并且都是使用 Base58Check 编码成 ASCII 码呈现:

  • 私钥 (private key)
  • 公钥 (public key)
  • 地址 (address)

比特币地址生成的过程

 

第一步:私钥 (private key)产生

  伪随机数产生的256bit私钥示例:

  8F72F6B29E6E225A36B68DFE333C7CE5E55D83249D3D2CD6332671FA445C4DD3

第二步:公钥 (public key)

  1. 拿私钥产生256bit私钥经secp256k1椭圆曲线推出的公钥(前缀04+X公钥+Y公钥):

  04

  06CCAE7536386DA2C5ADD428B099C7658814CA837F94FADE365D0EC6B1519385

  FF83EC5F2C0C8F016A32134589F7B9E97ACBFEFD2EF12A91FA622B38A1449EEB  

       2. 计算公钥的 SHA-256 哈希值(32bytes):

  2572e5f4a8e77ddf5bb35b9e61c61f66455a4a24bcfd6cb190a8e8ff48fc097d

       3. 取上一步结果,计算 RIPEMD-160 哈希值(20bytes):

  0b14f003d63ab31aef5fedde2b504699547dd1f6

       4. 取上一步结果,前面加入地址版本号(比特币主网版本号“0x00”):

  000b14f003d63ab31aef5fedde2b504699547dd1f6

  5. 取上一步结果,计算两次 SHA-256 哈希值(32bytes)

  ---1--- ddc2270f93cc84cc6869dd373f3c340bbf5cb9a8f5559297cc9e5d947aab2536

  ---2--- 869ac57b83ccf75ca9da8895823562fffb611e3c297d9c2d4612aeeb32850078

  6. 取上一步结果的前4个字节(8位十六进制)

  869ac57b  

  7. hash of public key,把这4个字节加在第4步的结果后面,作为校验(这就是比特币地址的16进制形态)

  000b14f003d63ab31aef5fedde2b504699547dd1f6869ac57b  

第三步:地址 (address)

  最终给用户使用的:用base58编码变换一下地址(这就是最常见的比特币地址形态)

  121bWssvSgsA9SKjR4DbYncEAoJjmBFwog

 

如何生成私钥(private key)

本质上私钥就是一串随机选出的 256 个 bit 的 01 数字(32 字节 * 8 = 256 bits),但是这串数字却控制着你的比特币账号的所有权,因此这串数字相当重要,要具有足够的随机性,一般采用密码学安全的伪随机数生成器(CSPNG),并且需要有一个来自具有足够熵值的源的种子(seed)。

比特币客户端软件使用 Secp256k1ECDSA 标准生成椭圆曲线,使用椭圆生成一个私钥,然后再从私钥中生成对应的公钥。

如果自行选取简单的123456密码,最终生成的账户可是“公交车账户”了哈哈。

选择 32 个字节的原因是因为 Bitcoin 使用的是 ECDSA 算法,并且使用的是 secp256k1 曲线。

 

如何生成公钥(public key)

Bitcoin 的公钥是通过 椭圆曲线密码学算法(K = k * G)来生成,其中公式中的:

  • K:公钥;
  • k:私钥,为上一段生成的 32 字节的字节数组(16 进制串表示);
  • G:为一个生成点;

Bitcoin 使用了 secp256k1 标准定义的一种特殊的椭圆曲线和一系列的数学常量。

如上公式,以私钥 k 为起点,与预定的生成点 G 相乘来生成公钥 K,并且因为所有 Bitcoin 用户的生成点 G 都是相同的(常量),所以由一个确定的私钥 k 生成一个确定的公钥 K,并且是单向的。

# 私钥生成公钥示例(非压缩公钥)
private key: de97fdbdb823a197603e1f2cb8b1bded3824147e88ebd47367ba82d4b5600d73
public key:  047c91259636a5a16538e0603636f06c532dd6f2bb42f8dd33fa0cdb39546cf449612f3eaf15db9443b7e0668ef22187de9059633eb23112643a38771c630db911

 

压缩格式的公钥 (compressed public keys)

简单来说,对于压缩公钥生成地址时,则只取非压缩公钥的X部分即可。

从上面的输出示例中可以看到 public key 一共有 130 个 16 进制的字符,共 520 个字节,其中的前缀为 04,这里的 04 表示该公钥为 非压缩格式,即完整存储了 x 和 y 坐标(各 256 个 bits),但是从 secp256k1 的椭圆曲线方式可以看到,只要知道其中一个坐标值,另外一个坐标值都是可以通过解方程得出的,因为可以只存储其中一个坐标,这样就可以节约 256 个 bits,从而引入了 压缩格式 的公钥。

上面的 04 前缀表示 非压缩格式,如果为压缩格式,则前缀为 02 或 03,有两个前缀主要是因为方程(y² = x³ + ax + b)的左侧的 y 为平方根,可能为正或者为负。

如下为一个与上面示例对应的压缩格式的公钥值:

private key:            de97fdbdb823a197603e1f2cb8b1bded3824147e88ebd47367ba82d4b5600d73
public key compressed:  037c91259636a5a16538e0603636f06c532dd6f2bb42f8dd33fa0cdb39546cf449

 

如何生成地址(公钥哈希 hash of public key)

Bitcoin 的地址由公钥经过单向的加密哈希算法 SHA256 和 RIPEMD160 生成,公式如下:

A = RIPEMD160(SHA256(K))

其中:
K 为公钥
A 为最终生成的地址;

# 生成的地址示例,地址的长度为 40 个 16 进制串,即 160 个bits:
private key:            de97fdbdb823a197603e1f2cb8b1bded3824147e88ebd47367ba82d4b5600d73
public key compressed:  037c91259636a5a16538e0603636f06c532dd6f2bb42f8dd33fa0cdb39546cf449
address:                52dab5e951ef4848a31b7ead8437df8184acbc54

公钥哈希就是大家看到的比特币地址,大部分比特币地址由公钥通过 base58Check 编码而来,把公钥地址从 512-Bit 哈希到 160-Bit ,但并不是所有的比特币地址都是公钥推导出来,也有可能是通过脚本建立在比特币网络中的虚拟币(比如彩色币)的脚本标识。

 

Base58, Base58Check 以及压缩格式

我们通常看到的 Bitcoin 地址都是经过 Base58Check 编码后的地址,Base58Check 编码也用于私钥,加密的密钥以及脚本中,用来提高可读性和录入的正确性。

下图描述了通过 公钥生成 Base58Check 编码格式的地址的整个过程:

其中 Public Key Hash 我们在上面已经生成的地址,之后就是通过 Base58Check 编码生成 Bitcoin 的地址格式。

(摘自 wiki)相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/"符号。
设计Base58主要的目的是:

    • 避免混淆。在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似。
    • 不使用"+“和”/"的原因是非字母或数字的字符串作为帐号较难被接受。
    • 没有标点符号,通常不会被从中间分行。
    • 大部分的软件支持双击选择整个字符串。 

base58符号映射表

为了进一步增加安全性,Base58Check 格式又在 Base58 的基础上新增了内置检查错误的校验和(checksum),该校验和是添加到末尾的额外 4 个字节,校验和的生成算法如下:

checksum = SHA256(SHA256(prefix+data))
  • data 为原始的数据;
  • prefix 前缀是个版本字段,是用来识别编码的数据的类型,如:Bitcoin 地址(也是 public key hash)的前缀为 0(即 0x00),当前支持的如下类型:

 

其中 私钥 的前缀为 128(即 0x80),对应的编码后前缀为 5,如下为我们的私钥编码后的:

// base58编码
de97fdbdb823a197603e1f2cb8b1bded3824147e88ebd47367ba82d4b5600d73
5KWKSRnmzxCjUP1NKR4dNyyHhaZWSGRTbGzBnm1vwgwpoe2AVGQ

 

下面为公钥以及生成的 Base58Check 格式的地址信息:

// base58编码
public key compressed:    037c91259636a5a16538e0603636f06c532dd6f2bb42f8dd33fa0cdb39546cf449
checksum: 4caf1695
base58check address:      18Z6R1VF7Do8RTHneeGzdVdbgjtXDVPmfS

 

base58check address:  18Z6R1VF7Do8RTHneeGzdVdbgjtXDVPmfS 
便于对应上面理解我们可以对比看看base58解码后的BTC地址:
Version  Public key hash                           Checksum
00       62E907B15CBF27D5425399EBF6F0FB50EBB88F18  C29B7D93

 

 

Base58Check 的编码过程:

因此实际上在 bitcoin 中大多数需要向用户展示的数据都是使用的 Base58Check 编码格式。

 

 

笔记总结参考:

学习 btc 钱包私钥、公钥和地址的生成过程

比特币密钥生成规则及 Go 实现