这集来聊如何生成一个比特币钱包,具体来说包含两项:地址和私钥。文末的参考资料给出了详细的操作步骤,我们这里的内容会比较概要,主要是帮助大家理解公钥密码学和钱包生成过程的关系,钱包地址和比特币私钥的关系。

生成私钥

生成钱包要从生成私钥开始,钱包地址可以通过私钥运算出来。选择私钥的过程就是在一个限定范围内,随机选择一个数。

那么这个范围是什么呢?答案非常简单,第一,私钥必须是32个字节,也就是说是32乘以8位的二进制数。实际使用中,私钥可以表示为二进制格式、八进制格式、Base64格式、WIF格式或者助记词格式。不管什么样的格式,底层都对应相同的256位二进制数。第二,私钥必须是正数并且小于 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,这是一个非常大的数了,绝大部分的256位二进制数都要比它小。

那么为什么会有上面的限制范围呢?这是因为比特币通过私钥运算公钥的时候会使用一套名为 ECDSA 的签名算法,全称是椭圆曲线数字签名算法。ECDSA 还可以选择不同的曲线,比特币选择的曲线被叫做 secp256k1。签名算法决定了私钥的选择范围。

生成私钥的时候唯一要考虑的就是安全问题。例如,通过 Python 语言 random.getrandbits(256) 就可以生成256位的随机数,就可以当私钥用了,但是这样生成的随机性其实只取决于生成时间,所以如果攻击者能够猜到你生成这个私钥的大致时间就会相对容易的通过暴力搜索的形式获得私钥。而采用 secrets.randbits(256) 就比较安全一些,因为随机性取决于系统上的一些不可复现的行为,即使我把自己的系统给你,你也不可能重新获得我的私钥。实际中,有人会到 https://www.bitaddress.org 上面生成私钥,这里的随机性取决于我们用鼠标在屏幕上随意滑动的轨迹。

总之,私钥只要满足范围,并且保证别人肯定获取不到就可以了。

计算公钥

有了私钥就可以通过 ECDSA 算法来生成比特币公钥了。

首先,我们需要把私钥传入 ECDSA 算法,这样得到的结果是一个64字节的整数,这个数是由长度均为32字节的一个点的 X 值和 Y 值拼接到一起得到的,例如,1e7bcc70c72770dbb72fea022e8a6d07f814d2ebe4de9ae3f7af75bf706902a7b73ff919898c836396a6b0c96812c3213b99372050853bd1678da0ead14487d7。

接下来,由这个结果获得比特币的完整公钥是非常容易的,只需要在结果的最开头加上 0x04,也就是 041e7bcc70c72770dbb72fea022e8a6d07f814d2ebe4de9ae3f7af75bf706902a7b73ff919898c836396a6b0c96812c3213b99372050853bd1678da0ead14487d7。

完整公钥看起来很长,所以可以压缩一下。我们知道公钥其实是一个点的 X 值和 Y 值拼接而成的,现在已知这条曲线,那么给定一个 X 值,只可能有正负两个 Y 值处在这条曲线上,所以我们可以从完整公钥里面把 Y 值剔除,只保留 Y 值的正负号。这样,如果以后需要的话,我们就可以很容易的运算出 Y 值来了。具体的做法是这样的,我们从完整公钥之中取出 X 值,如果 Y 值的最后一个字节是偶数,那么在 X 值的开头添加 0x02。如果 Y 值的最后一个字节是奇数,则在 X 值的开头添加 0x03。所以,我们当前情况下得到的压缩公钥就是 031e7bcc70c72770dbb72fea022e8a6d07f814d2ebe4de9ae3f7af75bf706902a7。

这就是计算公钥的过程,主要就是对私钥进行了椭圆曲线运算。以前的钱包软件通常会使用完整公钥,而现在大部分钱包都使用压缩公钥了。

获得地址

比特币的地址是通过公钥进行了一系列的转换而获得的,其中主要的是进行了多重的哈希运算。

第一步,对公钥进行加密。这里的公钥既可以是完整版,也可以是压缩版,我们选择压缩版。有了公钥之后,对公钥进行两次哈希运算,第一次通过 SHA-256 算法得到运算结果后,对结果再进行一次 RIPEMD-160 运算,最终得到的结果就是所谓的加密版的公钥了453233600a96384bb8d73d400984117ac84d7e8b。

第二步,对加密版公钥添加网络标识字节。比特币一共有两个网络:主网和测试网。如果我们需要生成一个主网地址,就要在加密版公钥开头添加 0x00,得到的结果是 00453233600a96384bb8d73d400984117ac84d7e8b。

第三步,添加校验值。校验值是通过对第二步得到的结果运行两次 SHA-256 哈希运算,然后取最终哈希值的前四个字节得到的,表示成十六机制就是 512f43c4。把这个校验值添加到第二步结果的末尾,得到的就是钱包地址了,也就是 00453233600a96384bb8d73d400984117ac84d7e8b512f43c4。有了校验值,钱包软件就很容易帮我们判定地址有没有填错或者损坏了。

但是,很多时候我们看到的钱包地址不是用十六进制表示的,而是用 Base58 格式,所以看起来可能是这个样子 17JsmEygbbEUEpvt4PFtYaTeSqfb9ki1F1。

总结

最后来总结一下,生成钱包的过程主要分了这么几步:第一步,生成一个32字节的私钥。第二步,通过椭圆曲线签名算法由私钥获得公钥。第三步,公钥经过一系列的转换得到钱包地址。注意,转换过程中采用了不可逆的哈希运算,所以从地址是不能够反向运算出公钥的。

参考: