前面一篇文章中提到的技术解决了交易信息不能被篡改的问题。但还有一个比较重要的问题,那就是,我们每个人只能发起和自己有关的交易,也就是能发起自己对别人付钱的交易,我们不能发起别人对我付钱,或是别人向别人付钱的交易。

那么,在比特币中是怎么解决这个问题的?让我们先看一些基础的加密技术。

比特币的加密方法

密钥对 / 签名 / 证书

所谓密钥对,也就是一种非对称加密技术。这种技术,在对信息进行加密和解密时,使用两个不同的密钥。这样一来,我们就可以把其中一个密钥公布出去,称之为公钥,另一个密钥私密地保管好,称之为私钥。

现实社会中,有人使用公钥加密,私钥解密,也有反过来用私钥加密,公钥解密,这得看具体的场景。(比特币使用了非对称加密的技术,其使用了ECDSA 密钥对比技术。)

比如,我把我加密的密钥发布给所有人,然后大家都用这个公钥加密信息,但其他人没有私钥,所以他们解不了密文,只有我能解密文,也只有我能看得懂别人用我的公钥加密后发给我的密文。如下图所示。

但是,这会有个问题,那就是每个人都有我的公钥,别人可以截获 Mike 发给我的信息,然后自己用我的公钥加密一个别的信息,伪装成 Mike 发给我,这样我就被黑了。于是,我们需要对 Mike 的身份进行验证,此时就需要用到“数字签名”的概念了。

Mike 也有一对密钥对,一个公钥给了我,私钥自己保留。

  • Mike 发自己想要的信息,做个 SHA 或 MD5 的 hash,得到一个 hash 串,又叫 Digest。
  • Mike 用自己的私钥,把 Digest 加密,得到一段 Digest 的密文。我们把这个事叫数字签名,Signature。
  • 然后,Mike 把他想发给我的信息用我的公钥加密后,连同他的数字签名一同发给我。
  • 我用我的私钥解密 Mike 发给我的密文,然后用 Mike 的公钥解密其数字签名得到 Digest。然后,我用 SHA 或 MD5 对解开的密文做 Hash。如果结果和 Digest 一致,就说明,这个信息是 Mike 发给我的,没有人更改过。

这个过程如下图所示。

但是问题还没完。假设有个黑客偷偷地把 Jack 电脑上的 Mike 的公钥给换了,换成自己的,然后截获 Mike 发出来的信息,用自己的密钥加密一段自己的信息,以及自己的数字签名。

于是,对于 Jack 来看,因为他用了黑客的公钥,而不是 Mike 的,那么对他来说,他就以为信息来自 Mike,于是黑客可以用自己的私钥伪装成 Mike 给 Jack 通信。反之亦然,于是黑客就可以在中间伪装成 Jack 或 Mike 来通信,这就是中间人攻击。如下图所示。

这个时候就比较麻烦了。Mike 看到有人在伪造他的公钥,想了想,他只能和 Jack 找了个大家都相信的永不作恶的权威的可信机构来认证他的公钥。这个权威机构,用自己的私钥把 Mike 的公钥和其相关信息一起加密,生成一个证书。

此时,Jack 就可以放心地使用这个权威机构的证书了。Mike 只需要在发布其信息的时候放上这个权威机构发的数字证书,然后 Jack 用这个权威机构的公钥解密这个证书,得到 Mike 的公钥,再用 Mike 的公钥来验证 Mike 的数字签名。

上面就是整个密钥对、签名和证书的全部基础细节。比特币也用了这样的基础技术来认证用户的身份的。下面,我们来看看比特币的一些细节。

比特币的加密

在比特币的世界里,每一笔交易的 From 和 To 都是每个用户的公钥(Public Key)。也就是说,使用用户的公钥来做交易的账户。于是,这个过程很简单。

  • 交易的发起方只能是支付方,支付方需要用自己的私钥来加密交易信息并制作相关的交易签名。
  • 网络上其他人会用你的公钥(也就是交易的支出方)来做解密来验证。

为什么不需要那个证书机构呢?不怕中间人攻击吗?这是因为,如果黑客想要伪造一笔别人的交易,那么他需要换掉半数以上结点上的被攻击者的公钥,这不太现实。与其这样做,还不如去偷被攻击者的私钥,可能还简单一些。

下面是一个交易链的图示。这个交易链的钱从 A -> B -> C -> D,一共 3 笔交易。

图片来源:Ken Shirriff Blog

  • 发起交易。我们从第一笔交易可以看到,A 用自己的私钥为交易信息和自己的地址生成了交易的签名,然后把交易信息、自己的地址、交易签名和自己的公钥放出去,这样方便别人来验证的确是 A 发起的。
  • 验证交易。在验证时,使用 A 的公钥解密交易签名,得到交易的 hash 值。把交易信息和自己的地址做 hash,看看是不是和签名解密后的 hash 值一致。

这里需要注意一个细节,比特币的地址是由我们的公钥生成的,生成规则比较复杂,可以参看 Bitcoin 的 Wiki 页 - Technical background of version 1 Bitcoin addresses

比特币的挖矿

前面说到,在比特币的区块 hash 算法中,要确保下面这个公式成立:

1
2

SHA-256(SHA-256 (Block Header)) < Target

而在区块头中,可以完全自由修改的只有一个字段,就是 Nonce,其他的 Timestamp 可以在特定范围内修改,Merkle Root 和你需要记录的交易信息有关系(所有的矿工可以自由地从待确认交易列表中挑选自己想要的交易打包)。

所以,基本上来说,你要找到某个数字,让整个 hash 值小于 Target。这个 Target 是一个数,其决定了,我们计算出来的 hash 值的字符串最前面有几个零。我们知道,hash 值本身就是一串相对比较随机的字符串。但是要让这个随机的字符串有规律,是一件很困难的事,除了使用暴力破解,没有其他办法。在计算机世界里,我们把这个事叫 " 哈希碰撞 “(hash collision),碰撞前几个位都是 0 的哈希值。

下面是一个示例。我想找到一个数,其和 “ChenHao” 加起来被 hash 后的值前面有 5 个零。

测试程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

import hashlib

 data="ChenHao"

 n=1

while n < 2**32:

    str = data + `n`

    hash = hashlib.sha256(str).hexdigest()

    hash = hashlib.sha256(hash).hexdigest()

    if hash.startswith('00000'):

        print  str, hash

        break

    n = n + 1

这是一个暴力破解的算法。这个程序在我的 MacBook Pro 上基本要 10 秒钟才跑得出来结果。

找到 1192481 时,找到了第一个解,如下所示:

1
2
3
4

ChenHao1192481   

00000669e0eeb33ee5dbb672d3bd2deb0c32ef9879ef260f0debbdcb80121160

那么,控制前面有多个 0 的那个 Target 又是怎么来的呢?是由 Bits 这个字段控制的,也就是难度系数,前面需要的 0 越多,难度也就越大。其中的算法你可以看一下 Bitcoin 的 Wiki 上的Difficulty 词条,这里我就不多说了。

这个难度系数,会在每出 2016 个区块后就调整一次。现在,这个难度是要在前面找到有 18 个零。如下所示 (一个真实的区块链的 Hash 值)

000000000000000000424118cc80622cb26c07b69fbe2bdafe57fea7d5f59d68

** 一个 SHA-256 算法算出来的哈希值有 22562256