主页 > imtoken钱包官网客服 > 《精通比特币》解读第4章——密钥、地址
《精通比特币》解读第4章——密钥、地址
本章主要讲私钥、公钥、比特币地址
私钥、公钥和比特币地址的关系如下图所示:
私钥
什么是私钥:私钥是一个随机生成的256位数字
私钥是如何生成的:生成比特币私钥本质上与“选择一个介于 1 和 2^256 之间的数字”是一样的。 只要选择的结果不可预测或不可重复,选择数字的确切方法并不重要。 可以用硬币、铅笔和纸随机生成你的私钥:抛一枚硬币256次,用纸笔记录正反面转换成0和1,就可以使用随机的256位二进制数作为比特币钱包的私钥。 比特币软件使用操作系统底层的随机数生成器来生成 256 位的熵。
私钥的作用:私钥可以进一步生成公钥。 私钥控制相应比特币地址中的所有资金。
私钥空间大小:2^256,约1.158 * 10^77
使用 Bitcoin Core 客户端生成新密钥:
使用获取新地址命令。 为了安全起见,命令运行后只显示生成的公钥,不显示私钥。 如果想让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 Integrity Primitives Evaluation Message Digest (RIPEMD),特别是 SHA256 和 RIPEMD160。
以公钥K为输入,计算其SHA256哈希值,并根据结果计算RIPEMD160哈希值,得到一个长度为160位(20字节)的数:
A = RIPEMD160(SHA256(K))
式中,K为公钥,A为生成的比特币地址。
通常,用户看到的比特币地址是经过“Base58Check”编码的。 这种编码使用了58个字符(一种Base58数字系统)和校验码,提高了可读性,避免了歧义,有效地防止了地址在转录和打字时的错误。
下图描述了如何从公钥生成比特币地址:
Base58和Base58Check编码
Base58:Base64中不包含0(数字0)、O(大写字母o)、l(小写字母L)、I(大写字母i)和“+”、“/”两个字符
Base58Check:是比特币常用的一种Base58编码格式,内置了一种错误校验编码(checksum),可以防止写入错误和转录错误。
功能:当使用Base58check编码时,解码软件会计算数据的校验和,并与编码中包含的校验和进行比较。 如果两者不匹配,就会出错,Base58Check数据无效。 错误的比特币地址不会被钱包软件认为是有效地址,否则这种错误会造成资金的损失。
base58check编码过程:
1、首先我们需要给数据添加一个前缀,叫做“version byte”,用来标识编码数据的类型。
2、接下来计算“double hash”校验和,也就是对前面的结果(前缀和数据)运行两次SHA256哈希算法:
校验和 = SHA256(SHA256(前缀+数据))
在生成的 32 字节长哈希(两次哈希操作)中,我们只取前 4 个字节。 这 4 个字节用作检查错误的代码或校验和。 数据后将添加校验和。
3、结果由前缀、数据、校验和三部分组成。 然后如前所述使用 Base58 对该结果进行编码。
下图描述了Base58Check编码的过程:
在比特币中,大部分需要展示给用户的数据都是使用Base58Check编码的,可以实现数据压缩、可读性和错误校验。
Base58Check 编码中的版本前缀用于创建易于区分的格式。 版本前缀编码后,一些特殊的字符可以让用户轻松识别编码数据的类型以及如何使用。 显示了一些版本前缀及其对应的 Base58 编码字符:
回顾一下比特币地址生成的完整过程,从私钥,到公钥(椭圆曲线上的一个点),再到二次哈希地址,再到最后的Base58Check编码。 以下 C++ 代码详细显示了从私钥到 Base58Check 编码的比特币地址的步骤。 代码使用了“3.3 其他客户端、数据库和工具包”一节中介绍的libbitcoin库,实现了一些辅助功能:
#include <bitcoin/bitcoin.hpp>
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;
}
编译并运行地址代码:
# 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
}
结果包含作为有效负载的密钥、WIF 版本前缀 128 和校验和。
2.使用base58check-decode命令解码压缩密钥:
$ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
wrapper
{
checksum 2339607926
payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01
version 128
}
注意压缩密钥的“payload”后缀为01,表示导出的公钥是要压缩的。
将十六进制密钥转换为 Base58Check 编码
使用比特币浏览器的 base58check-encode 命令
bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd --version 128
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
将十六进制密钥(压缩格式密钥)转换为 Base58Check 编码
需要在十六进制私钥后面加上后缀01,然后使用同上方法:
$ 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 位数字(130 个十六进制数字)的相同公钥。 这个 520 位数字以前缀 04 开头,后跟 x 和 y 坐标,格式为 04 xy:
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
压缩格式的私钥:
其实“压缩格式私钥”是一个误导性的名称,私钥是未压缩的,不能压缩。 “压缩私钥”实际上只是指“用于生成压缩格式公钥的私钥”,而“未压缩格式私钥”用于表示“用于生成未压缩格式公钥的私钥”。
下图显示了相同的私钥,但格式不同: