每天进步一点点:学习用公钥验证

昨天啃了ecdsa用私钥签名摘要,对这部分多了一些了解(其中我并不清楚签名过程中到底做了什么,这部分更加深奥,不去啃了),今天继续学习如何用公钥验证。


(图源 :pixabay)

简单的示例

首先最简单的签名与验证代码如下:

from ecdsa import SigningKey
sk = SigningKey.generate(curve=ecdsa.SECP256k1)
vk = sk.verifying_key
signature = sk.sign(b"message")
print(vk.verify(signature, b"message"))

在上述代码中,我们用SigningKey对message这个消息签名,然后用verifying_key对签名进行校验。

但是我们的例子略微复杂,所以我们先来回顾一下我们如何签名的。

回顾签名

在我们之前的代码中,是针对消息摘要进行的签名,并且SigningKey是使用我们之前文章中生成的私钥。

message = "Hello world!"
digest = hashlib.sha256(bytes(message, 'utf-8')).digest()
private_key = "415ac848c316b406920e0a4b43adc7f93c45bb89124f80ced8d1f50fae4f080d"
sk = ecdsa.SigningKey.from_string(unhexlify(private_key), curve=ecdsa.SECP256k1)

并且根据回调函数(sigencode)的不同,ecdsa.util.sigencode_der 或者ecdsa.util.sigencode_string分别得出sigder以及sigstr两种格式的签名:

k = 1
sigder = sk.sign_digest(digest,sigencode=ecdsa.util.sigencode_der, k=k)
sigstr = sk.sign_digest(digest,sigencode=ecdsa.util.sigencode_string, k=k)

签名验证

因为公钥本来就是从私钥生成的,所以我们可以直接从SigningKey来获得VerifyingKey:

vk = sk.get_verifying_key()

然后我们就可以使用VerifyingKey来验证签名了:

print(vk.verify_digest(sigstr, digest, sigdecode=ecdsa.util.sigdecode_string)) # True
print(vk.verify_digest(sigder, digest, sigdecode=ecdsa.util.sigdecode_der)) # True

需要注意的是生成签名时的回调函数为sigencode_xxx,而校验签名时的回调函数为:sigdecode_xxx,encode和decode不细看区别不出来,不过代码里写错了可就妥妥地要出错了。

如果我们看一下代码,就不难发现,无论是der还是str格式的签名,都要先恢复成r,s的,这就是decode回调的意义所所在了:

Reveal spoiler

image.png

再谈公钥

我们的结论是公钥是从私钥生成的,比如上述代码:

vk = sk.get_verifying_key()

我们可以将vk打印出来:

Reveal spoiler

image.png

不能发现,其实和我们之前用私钥生成的公钥完全一样的(非压缩,不带前缀):

f5810b31e23d76a1b8a76cfe43d7168abbaf6363c3927aafaf4751697488d329147d6d4e232cf7de60d94b3962ea260894fe02274223b2a3050eb9b7e655ab4e

其实我们也可以直接用公钥生成VerifyingKey

public_key = "f5810b31e23d76a1b8a76cfe43d7168abbaf6363c3927aafaf4751697488d329147d6d4e232cf7de60d94b3962ea260894fe02274223b2a3050eb9b7e655ab4e"
vk = ecdsa.VerifyingKey.from_string(unhexlify(public_key), curve=ecdsa.SECP256k1)
print(hexlify(vk.to_string()).decode())

这个输出的vk和之前的是一样的,其实这才是vk的打开正确方式,因为校验的时候,我们往往都是只有公钥没有私钥的

相关链接