数字签名

到目前为止,我们还没有深入探讨“数字签名”的细节。在本节中,我们将探讨数字签名是如何工作的,以及如何在不泄露私钥的情况下提供私钥所有权的证明。

椭圆曲线数字签名算法(ECDSA)

以太坊中使用的数字签名算法是_Elliptic Curve Digital Signature Algorithm_,或_ECDSA_。ECDSA是用于基于椭圆曲线私钥/公钥对的数字签名的算法,如 [elliptic_curve] 中所述。

数字签名在以太坊中有三种用途(请参阅下面的边栏)。首先,签名证明私钥的所有者,暗示着以太坊账户的所有者,已经授权支付ether或执行合约。其次,授权的证明是_undeniable_(不可否认)。第三,签名证明交易数据在交易签名后没有也不能被任何人修改。

Wikipedia对“数字签名”的定义

数字签名是用于证明数字信息或文件真实性的数学方案。有效的数字签名使收件人有理由相信该信息是由已知的发件人(认证)创建的,发件人不能否认已发送的信息(不可否认),并且信息在传输过程中未被更改(完整性) 。 来源: https://en.wikipedia.org/wiki/Digital_signature_

数字签名如何工作

数字签名是一种数学签名,由两部分组成。第一部分是使用私钥(签名密钥)从消息(交易)中创建签名的算法。第二部分是允许任何人仅使用消息和公钥来验证签名的算法。

创建数字签名

在以太坊实现的ECDSA中,被签名的“消息”是交易,或者更确切地说,来自交易的RLP编码数据的Keccak256哈希。签名密钥是EOA的私钥。结果是签名:

\(\(Sig = F_{sig}(F_{keccak256}(m), k)\)\)

其中:

  • _k_是签名私钥

  • _m_是RLP编码的交易

  • F**keccak256 是Keccak256哈希函数

  • F**sig 是签名算法

  • Sig 是由此产生的签名

更多关于ECDSA数学的细节可以在 ECDSA数学 中找到。

函数 F**sig 产生一个由两个值组成的签名+Sig+,通常称为+R+和+S+:

  1. Sig = (R, S)

验证签名

要验证签名,必须有签名(R+和+S),序列化交易和公钥(与用于创建签名的私钥对应)。实质上,对签名的验证意味着“只有生成此公钥的私钥的所有者才能在此交易上产生此签名。”

签名验证算法采用消息(交易的散列或其部分),签名者的公钥和签名(+R+和+S+值),如果签名对此消息和公钥有效,则返回TRUE。

ECDSA数学

如前所述,签名由数学函数 F**sig 创建,该函数生成由两个值_R_和_S_组成的签名。在本节中,我们将更详细地讨论函数 F**sig

签名算法首先生成_ephemeral_(临时的)私钥/公钥对。在涉及签名私钥和交易哈希的转换之后,此临时密钥对用于计算_R_和_S_值。

临时密钥对由两个输入值生成:

1.一个随机数_q_,用作临时私钥 1.和椭圆曲线生成点_G_

从_q_和_G_开始,我们生成相应的临时公钥_Q_(以_Q = q * G_计算,与以太坊公钥的派生方式相同,参见[pubkey])。数字签名的_R_值就是临时公钥_Q_的x坐标。

然后,算法计算签名的_S_值,以便:

Sq-1 (Keccak256(m) + k * R) (mod p)

其中:

  • _q_是临时私钥

  • _R_是临时公钥的x坐标

  • _k_是签名(EOA所有者)的私钥

  • _m_是交易数据

  • _p_是椭圆曲线的素数阶

验证是签名生成函数的反函数,使用_R_,S_值和公钥来计算一个值_Q,它是椭圆曲线上的一个点(签名创建中使用的临时公钥):

QS-1 * Keccak256(m) * G + S-1 * R * K (mod p)

其中:

  • _R_和_S_是签名值

  • _K_是签名者(EOA所有者)的公钥

  • _m_是被签名的交易数据

  • _G_是椭圆曲线生成点

  • _p_是椭圆曲线的素数阶

如果计算的点_Q_的x坐标等于_R_,则验证者可以断定该签名是有效的。

请注意,在验证签名时,私钥既不被知道也不会透露。

Tip

ECDSA必然是一门相当复杂的数学; 完整的解释超出了本书的范围。许多优秀的在线指南会一步一步地通过它:搜索“ECDSA explained”或尝试这一个:http://bit.ly/2r0HhGB[]。

实践中的交易签名

为了产生有效的交易,发起者必须使用椭圆曲线数字签名算法将数字签名应用于消息。当我们说“签署交易”时,我们实际上是指“签署RLP序列化交易数据的Keccak256哈希”。签名应用于交易数据的哈希,而不是交易本身。

Tip

在#2,675,000块,Ethereum实施了“Spurious Dragon”硬分叉,除其他更改外,还推出了包括交易重播保护的新签名方案。这个新的签名方案在EIP-155中指定(参见[eip155])。此更改会影响签名过程的第一步,在签名之前向交易添加三个字段(v,r,s)。

要在以太坊签署交易,发件人必须:

  1. 创建一个包含九个字段的交易数据结构:nonce,gasPrice,startGas,to,value,data,v,r,s

  2. 生成交易的RLP编码的序列化消息

  3. 计算此序列化消息的Keccak256哈希

  4. 计算ECDSA签名,用发起EOA的私钥签名散列

  5. 在交易中插入ECDSA签名计算出的 r 和 s 值

原始交易创建和签名

让我们创建一个原始交易并使用 ethereumjs-tx 库对其进行签名。此示例的源代码位于GitHub存储库中的 raw_tx_demo.js 中:

raw_tx_demo.js: Creating and signing a raw transaction in JavaScript

  1. link:code/web3js/raw_tx/raw_tx_demo.js[]

在此处下载: https://github.com/ethereumbook/ethereumbook/blob/develop/code/web3js/raw_tx/raw_tx_demo.js

运行示例代码:

  1. $ node raw_tx_demo.js
  2. RLP-Encoded Tx: 0xe6808609184e72a0008303000094b0920c523d582040f2bcb1bd7fb1c7c1ecebdb348080
  3. Tx Hash: 0xaa7f03f9f4e52fcf69f836a6d2bbc7706580adce0a068ff6525ba337218e6992
  4. Signed Raw Transaction: 0xf866808609184e72a0008303000094b0920c523d582040f2bcb1bd7fb1c7c1ecebdb3480801ca0ae236e42bd8de1be3e62fea2fafac7ec6a0ac3d699c6156ac4f28356a4c034fda0422e3e6466347ef6e9796df8a3b6b05bed913476dc84bbfca90043e3f65d5224

用EIP-155创建原始交易

EIP-155“简单重播攻击保护”标准在签名之前指定了重播攻击保护(replay-attack-protected)的交易编码,其中包括交易数据中的_chain identifier_。这确保了为一个区块链(例如以太坊主网)创建的交易在另一个区块链(例如Ethereum Classic或Ropsten测试网络)上无效。因此,在一个网络上广播的交易不能在另一个网络上广播,因此得名“重放攻击保护”。

EIP-155向交易数据结构添加了三个字段 v,r+和+s。r+和+s 字段被初始化为零。这三个字段在编码和散列_之前_被添加到交易数据中。因此,三个附加字段会更改交易的散列,稍后将应用签名。通过在被签名的数据中包含链标识符,交易签名可以防止任何更改,因为如果链标识符被修改,签名将失效。因此,EIP-155使交易无法在另一个链上重播,因为签名的有效性取决于链标识符。

签名前缀字段+v+被初始化为链标识符,其值为:

Chain

Chain ID

Ethereum main net

1

Morden (obsolete), Expanse

2

Ropsten

3

Rinkeby

4

Rootstock main net

30

Rootstock test net

31

Kovan

42

Ethereum Classic main net

61

Ethereum Classic test net

62

Geth private testnets

1337

由此产生的交易结构被进行RLP编码,哈希和签名。签名算法也稍作修改,以在+v+前缀中对链ID进行编码。

有关更多详细信息,请参阅EIP-155规范: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md