主页 > imtoken官网钱包app > 《精通比特币》第4章解读——密钥、地址

《精通比特币》第4章解读——密钥、地址

imtoken官网钱包app 2023-01-16 20:42:12

本章主要讲私钥、公钥和比特币地址

私钥、公钥和比特币地址的关系如下:

这里写图片描述

私钥

什么是私钥:私钥是一个随机生成的256位数字

如何生成私钥:生成比特币私钥本质上与“选择一个介于 1 和 2^256 之间的数字”。只要选择的结果不可预测或不可重复,选择数字的确切方法并不重要。你可以用硬币、铅笔和纸随机生成你的私钥:掷硬币 256 次,用纸和笔记录正反面并转换为 0 和 1比特币地址,随机 256 位二进制数可作为比特币钱包的私钥。比特币软件使用操作系统底层的随机数生成器生成256位熵。

私钥的作用:私钥可以进一步生成公钥。私钥控制着对应比特币地址的所有资金。

私钥空间大小:2^256,约1.158 * 10^77

使用比特币核心客户端生成新密钥:

p>

可以使用getnewaddress 命令。出于安全考虑,该命令运行后只显示生成的公钥,不显示私钥。如果你想让bitcoind显示私钥,可以使用dumpprivkey命令。

$ bitcoin-cli getnewaddress
1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
$ bitcoin-cli dumpprivkey 1J7mdg5rbQyUHENYdx39WVWK7fsL
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

公钥

什么是公钥:公钥是由私钥生成的一串数字和字母。

可以通过椭圆曲线乘法从私钥计算出公钥,这是一个不可逆的过程:K = k * G。其中 k 是私钥,G 是称为生成点的常数点,K 是生成的公钥。反向操作,称为“求离散对数”——知道公钥 K 的情况下求私钥 k——非常困难,就像尝试 k 的所有可能值一样,蛮力搜索。

比特币使用特殊的椭圆曲线和一系列由 secp256k1 标准定义的数学常数。

生成公钥:

比特币地址

从一个随机生成的私钥k开始,我们将它乘以曲线上预定的生成点G,得到曲线上的另一个点,即对应的公钥K。生成点是secp256k1标准的一部分,并且比特币密钥的生成点都是一样的。

{K = k * G}

其中k为私钥,G为生成点,曲线上生成的点K为公钥。由于所有比特币用户的生成点都相同,因此私钥 k 乘以 G 将得到相同的公钥 K。

因为数学是单向的,所以私钥可以转换为公钥,但公钥不能转换回私钥。

比特币地址

什么是比特币地址比特币地址是由公钥生成的一串数字和字母,以数字“1”开头。

示例:1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

比特币地址可以代表公钥/私钥对的所有者

如何生成比特币地址比特币地址可以通过单向加密哈希算法得到公钥。这些算法是安全散列算法 (SHA) 和 RACE 完整性原语评估消息摘要 (RIPEMD),特别是 SHA256 和 RIPEMD160。

以公钥K为输入,计算其SHA256哈希值,并用结果计算RIPEMD160哈希值,得到一个长度为160位(20字节)的数字:

A = RIPEMD160(SHA256(K))

公式中,K为公钥,A为生成的比特币地址

通常用户看到的比特币地址是用“Base58Check”编码的,它使用58个字符(一种Base58数字系统)和一个校验码来提高可读性,避免二义性,有效防止地址转录和输入错误。

下图描述了如何从公钥生成比特币地址

这里写图片描述

比特币地址

Base58 和 Base58Check 编码

Base58:Base64中Base58不包含0(数字0),O(大写o),l(小写L),I(大写i),以及两个字符“+”和“/”

Base58Check:是比特币常用的Base58编码格式,内置错误校验编码(checksum),防止写入错误和转录错误。

功能:使用Base58check编码时,解码软件会计算数据的校验和,并与编码自带的校验和进行比较。如果两者不匹配,则说明发生了错误,则Base58Check数据无效。一个错误比特币地址不会被钱包软件认为是一个有效地址,否则这个错误会导致资金损失。

Base58check编码过程:

首先我们需要在数据中添加一个称为“版本字节”的前缀,这个前缀是用来标识数据编码的类型。接下来,计算一个“双哈希”校验和,即对前面的结果(前缀和数据)运行两次 SHA256 哈希算法:

校验和 = SHA256(SHA256(前缀+数据))

在得到的 32 字节长哈希(两个哈希)中,我们只取前 4 个字节。这 4 个字节用作错误校验码或校验和。校验码会加在数据后面。结果由三部分组成:前缀、数据和校验和。然后结果Encoding with Base58 如前所述。

下图描述了Base58Check编码的过程:

这里写图片描述

在比特币中,大部分需要展示给用户的数据都是经过Base58Check编码的,可以实现数据压缩、易读和错误校验。

Base58Check 编码中的版本前缀用于创建易于识别的格式。编码后的版本前缀是一些特殊字符,方便用户识别被编码的数据类型以及如何使用。显示一些版本前缀及其对应的 Base58 编码字符:

这里写图片描述

回顾比特币地址生成的完整过程,从私钥,到公钥(椭圆曲线上的一个点),再到两次散列的地址,再到最终的 Base58Check 编码。以下 C++ 代码详细显示了从私钥到 Base58Check 编码的比特币地址的步骤。代码使用“3.3 Other Clients, Libraries, Toolkits”实现了一些辅助功能:

#include 
int main()
{
// Private secret key.
bc::ec_secret secret;
bool success = bc::decode_base16(secret,
"038109007313a5807b2eccc082c8c3fbb988a973cacf1a7df9ce725c31b14776");
assert(success);
// Get public key.
bc::ec_point public_key = bc::secret_to_public_key(secret);
std::cout << "Public key: " << bc::encode_hex(public_key) << std::endl;
// Create Bitcoin address.
// Normally you can use:
// bc::payment_address payaddr;
// bc::set_public_key(payaddr, public_key);
// const std::string address = payaddr.encoded();
// Compute hash of public key for P2PKH address.
const bc::short_hash hash = bc::bitcoin_short_hash(public_key);
bc::data_chunk unencoded_address;
// Reserve 25 bytes
// [ version:1 ]
// [ hash:20 ]
// [ checksum:4 ]
unencoded_address.reserve(25);
// Version byte, 0 is normal BTC address (P2PKH).
unencoded_address.push_back(0);
// Hash data
bc::extend_data(unencoded_address, hash);
// Checksum is computed by hashing data, and adding 4 bytes from hash.
bc::append_checksum(unencoded_address);
// Finally we must encode the result in Bitcoin's base58 encoding.
assert(unencoded_address.size() == 25);
const std::string address = bc::encode_base58(unencoded_address);
std::cout << "Address: " << address << std::endl;
return 0;
}

比特币地址

编译运行addr代码:

# Compile the addr.cpp code
$ g++ -o addr addr.cpp $(pkg-config --cflags --libs libbitcoin)
# Run the addr executable
$ ./addr
Public key: 0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa
Address: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK

密钥格式

公钥和私钥可以有多种格式。当一个密钥以不同的格式编码时,结果可能看起来不同,但它们都是用相同的数字编码的。这些不同的编码格式主要是为了方便人们正确使用和识别密钥。

私钥格式:

私钥可以用多种不同的格式表示,所有格式都对应同一个 256 位数字。

下表显示了三种常见的私钥格式:

这里写图片描述

在不同的场景中使用不同的格式。十六进制和原始二进制格式在软件内部使用,很少向用户展示。 WIF格式用于钱包之间密钥的输入和输出,也用于代表私钥的二维码(条形码)。

下表显示了生成的三种格式的私钥:

这里写图片描述

从 Base58Check 解码

使用 Bitcoin Explorer 命令解码 Base58Check 格式

1.使用 base58check-decode 命令解码未压缩的密钥:

$ bx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
wrapper
{
checksum 4286807748
payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
version 128
}

比特币地址

结果包含 Key 作为有效负载、WIF 版本前缀 128 和校验和。

2.使用base58check-decode命令对压缩后的密钥进行解码:

$ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
wrapper
{
checksum 2339607926
payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01
version 128
}

请注意,“Payload”附加了一个后缀 01,表示要压缩导出的公钥。

将十六进制密钥转换为 Base58Check 编码

使用 Bitcoin Explorer 的 base58check-encode 命令

bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd --version 128
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

将十六进制密钥(压缩格式密钥)转换为Base58Check编码

在十六进制私钥后面加上后缀01,然后使用同上方法:

p>

$ bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 --version 128
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

生成的WIF压缩格式的私钥以字母“K”开头,表示编码后的私钥有后缀“01”,私钥只能用于生成压缩格式的公钥(参见“压缩格式的公钥”部分)。

公钥格式

公钥也可以用多种不同的格式来表示,通常分为两种形式:未压缩格式或压缩格式公钥。

从上面我们知道,公钥是椭圆曲线上的一个点,由一对坐标(x,y)组成。公钥通常由前缀 04 后跟两个 256 位数字表示。其中一个 256 位数字是公钥的 x 坐标,另一个 256 位数字是 y 坐标。前缀04用于区分未压缩格式的公钥比特币地址,压缩格式的公钥以02或03开头。

比特币地址

未压缩的公钥:

以下是上一节中私钥生成的公钥,其坐标x和y如下:

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

同一个公钥用520位十六进制数表示(下面是同一个16进制数30位数字。520位的数字以开头04的情节组成,紧接着是x及y坐标,格式为04 x y:

K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE 52DDFE2E505BDB

压缩格式:

未压缩的公钥以04为前缀,而压缩的公钥以02或03为前缀。需要这两个不同前缀的原因是:

因为椭圆曲线加密公式的左边是y2,也就是说y的解来自一个平方根,可以是正的也可以是负的。更形象地说,y 坐标可以高于或低于 x 轴。当我们在素数p阶的有限域上用二进制算术计算椭圆曲线时,y坐标可能是奇数也可能是偶数,对应于上面提到的y值的正负号。因此,为了区分y坐标的两个可能值,当我们生成压缩格式的公钥时,如果y是偶数,我们就给它加上前缀02;如果y是奇数,我们在前面加上03。这样就可以根据公钥中给出的x值正确推导出对应的y坐标,从而将公钥解压成椭圆曲线上的一个完整点坐标。

下图说明了公钥压缩:

这里写图片描述

以下是上一节生成的公钥,采用264位(66位十六进制数字)压缩格式公钥格式,其中前缀03表示y坐标为奇数:

K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

压缩格式私钥:

其实“压缩格式私钥”是名不副实,私钥是未压缩的,无法压缩。 “压缩私钥”实际上只是“用于生成压缩格式的公钥的私钥”,而“未压缩的私钥”是指“用于生成未压缩格式的公钥的私钥”。

下图展示了不同格式的相同私钥:

这里写图片描述