比特币源码研读(5)—— 私钥、公钥和地址

比特币中的私钥、公钥和地址,类比我们现实生活中银行卡,私钥是密码,地址是卡号,公钥还没有恰当的类比。在转账过程中,我们有密码,知道对方的地址,就可以转账成功。谁拥有私钥,谁就可以花费这个钱;谁拥有发送地址对应的私钥,谁就能接收这笔钱。
在比特币种,私钥、公钥和地址三者的关系是这样的:

  1. 私钥:随机生成256位比特的二进制数字
  2. 公钥:由私钥通过椭圆曲线乘法生成
  3. 地址:公钥通过单向的加密哈希算法得到

如下图

私钥

比特币的私钥就是1~2^256中的一个数字,这个数字非常非常大,保证了比特币的私钥不会被破解。那到底2^256这个数字有多大呢?用十进制表示的话,大约是10^77,而可见宇宙被估计只含有10^80个原子。超级计算机“神威·太湖之光”的峰值性能为12.5亿亿次/秒,相当于10^17,如果用这台目前世界第一的超级计算来碰撞比特币私钥,需要10^60秒,也就是3*10^52年。我们可以得出这样的结论,通过碰撞破解比特币的私钥是不可能的。
比特币代码中生成私钥的方法是MakeNewKey,这个方法在文件key.cpp。循环通过GetStrongRandBytes通过伪随机数生成器生成私钥,然后检查是否符合要求,如果符合则跳出循环。

// 在key.cpp
void CKey::MakeNewKey(bool fCompressedIn) {
    do {
        GetStrongRandBytes(keydata.data(), keydata.size());
    } while (!Check(keydata.data()));
    fValid = true;
    fCompressed = fCompressedIn;
}
 
//在random.cpp
void GetStrongRandBytes(unsigned char* out, int num)
{
    assert(num <= 32);
    CSHA512 hasher;
    unsigned char buf[64];
 
    // First source: OpenSSL's RNG 
    RandAddSeedPerfmon();
    GetRandBytes(buf, 32);
    hasher.Write(buf, 32);
 
    // Second source: OS RNG 操作系统熵值
    GetOSRand(buf);
    hasher.Write(buf, 32);
 
    // Third source: HW RNG, if available.
    if (GetHWRand(buf)) {
        hasher.Write(buf, 32);
    }
 
    // Combine with and update state
    {
        std::unique_lock<std::mutex> lock(cs_rng_state);
        hasher.Write(rng_state, sizeof(rng_state));
        hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
        ++rng_counter;
        hasher.Finalize(buf);
        memcpy(rng_state, buf + 32, 32);
    }
 
    // Produce output
    memcpy(out, buf, num);
    memory_cleanse(buf, 64);
}

公钥

公钥是私钥通过椭圆曲线乘法生成的,只能通过私钥生成公钥,不能通过公钥逆推出私钥,这是不可逆转的过程。这个太过于复杂,等以后均益搞明白了,再详细说说。

地址

地址是通过公钥单向的加密哈希算法,这也是不可逆的。

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

A = RIPEMD160(SHA256(K))

在产生的长32个字节的哈希值(两次哈希运算)中,我们只取前4个字节。这4个字节就作为检验错误的代码或者校验和。校验码会添加到数据之后。
结果由三部分组成:前缀、数据和校验和。最后Base58编码。

Leave a Reply

Your email address will not be published. Required fields are marked *