数字签名的生成
假设Alice要给Bob发一个经过数字签名的消息,他们首先需要定义一组共同接受的椭圆曲线加密用参数,简单的,这组参数可表示为 (CURVE, G, n)
其中,CURVE表示椭圆曲线点域和几何方程;G是所有点倍积运算的基点;n是该椭圆曲线的可倍积阶数(multiplicative order),作为一个很大的质数,n的几何意义在于,nG = 0,即点倍积nG的结果不存在,而对于小于n的任何一个正整数 m = [1,n-1],点倍积mG都可以得到一个合理的处于该椭圆曲线上的点。
其次,Alice要创建一对钥,即一个私钥和一个公钥。私钥来自于[1, n-1]范围内一个随机数:dA = rand(1, n-1)
公钥如下,它来自私钥和基点的椭圆曲线点倍积:QA = dA X G
Alice想要对消息m作数字签名,有以下步骤:
- 计算
e = HASH(m)
,HASH是一个哈希加密函数,比如SHA-2,或SHA-3。 - 计算
z
,来自e的二进制形式下最左边(即最高位)L_n个bits,而L_n是上述椭圆曲线参数中的可倍积阶数n的二进制长度。注意z 可能大于n,但长度绝对不会比 n 更长。 - 从 [1, n-1] 内,随机选择一个符合加密学随机安全性的整数
k
。 - 计算一个椭圆曲线上点:
(x1, y1) = k X G
- 以下式计算 r 值, 如果r == 0, 则返回步骤3重新计算
r = x1 mod n
- 以下式计算 s 值,如果 s == 0,则返回步骤3重新计算
- 生成的数字签名就是
(r, s)
Alice 用自己的私钥和随机数 k 签名了哈希值 z。Bob 用 Alice 的公钥来验证签名的正确性
特别需要注意的是步骤3中 k 的选择,它不仅要满足加密学的随机安全性要求,要像私钥一样保护起来,更重要的是,在每次生成一个新的数字签名时,这个 k 必须每次都要更新。否则,通过上述数字签名过程中的算式相互换算,很容易从中破译出私钥!具体换算过程可见wiki_ECDSA。
ECDSA签名可能泄漏私钥的另一种方式是 {\ displaystyle k}ķ是由错误的随机数发生器产生的。这样的随机数生成失败导致Android比特币钱包的用户在2013年8月损失了资金
数字签名的验证
对于消息的接收方Bob来说,他除了收到数字签名文件外,还会有一份公钥。所以Bob的验证分两部分,首先验证公钥,然后验证签名文件(r, s)。
公钥的验证
- 公钥
QA
的座标应是有效的,不会等于一个极限值空点O
- 通过公钥
QA
的座标验证它必须是处于该椭圆曲线上的点。 - 应有下式成立,即曲线的可倍积阶数 n 与公钥的点倍积不存在
签名文件的验证
- 验证 r 和 s 均是处于[1, n-1]范围内的整型数;否则验证失败
- 计算
e = HASH(m)
,HASH()即签名生成过程步骤1中使用的哈希函数。 - 计算
z
,来自 e的最左边L_n个bits - 计算两个参数
u1
和u2
:
- 计算(x1, y1),如果(x1, y1)不是一个椭圆曲线上的点,则验证失败:
- 如果下面等式成立, 则签名有效
公钥恢复
收到消息m
和爱丽丝的签名r,s
,在该消息上,Bob可以(可能)恢复Alice的公钥:
- 验证
r
和s
是整数[1,n-1]
。如果不是,则签名无效。 - 计算曲线点
R=(x1, y1)
,x1
是其中之一 r, r+n, r+2n等(提供的x1
对于字段元素来说不是太大), y1是是满足曲线方程式的值。
请注意,可能有几个满足这些条件的曲线点,每个曲线点都不同R
值将产生一个不同的已恢复密钥。 - 计算
e = HASH(m)
,HASH()即签名生成过程步骤1中使用的哈希函数。 - 计算
z
,来自 e的最左边L_n个bits - 计算两个参数
u1
和u2
:
- 计算曲线点
- 如果QA匹配Alice的公钥, 则签名有效
- 如果尝试了所有可能的R点且没有匹配Alice的公钥,则签名无效。
请注意,无效的签名或来自其他消息的签名将导致恢复不正确的公钥。如果事先知道签名者的公钥(或其哈希),则恢复算法只能用于检查签名的有效性。
公式推导请参考: wiki_ECDSA
公链签名与验签算法
摘要生成 | 签名算法 | 签名数据格式 | 验签算法 | |
---|---|---|---|---|
BTC | SHA256 | ECDSA-secp256k1 | BIP66之后采用DER编码. 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] |
verify模式 OP_CHECKSIG ECDSA_CheckSignature (pubKeyStr,sigStr, sha256(sha256(verifyThisStr))) |
ETH | SHA3-Keccack | ECDSA-secp256k1 | (R,S,V) V是recid, 为0或者1, v := sig[0] - 27 |
recove模式pub, err := crypto.Ecrecover(sighash[:], sig) |
EOS | SHA256 | ECDSA-secp256k1|r1 | (V,R,S) V中带了recid v: 27 + 4 + recid , 从签名中提取出recove id: V - 4 - 27 |
recove模式recov = public_key_type( sig, digest ) |
公链 ECDSA 验签方式
用公钥去verify的方式:
-
BTC :
ECDSA_CheckSignature(pubKeyStr,sigStr,sha256(sha256(verifyThisStr)))
(参考资料: https://en.bitcoin.it/wiki/File:Bitcoin_OpCheckSig_InDetail.png )
-
Hyperledger Fabric
valid, err := id.msp.bccsp.Verify([id.pk](http://id.pk/), sig, digest, nil)
(参考资料: https://blog.csdn.net/idsuf698987/article/details/81677133 )
recove出公钥的方式:
- ETH
pub, err := crypto.Ecrecover(sighash[:], sig) //摘自源码
- EOS
recov = public_key_type( sig, digest ) //摘自源码