最近打算学习一下私钥/公钥/签名等相关内容,发现这块东西真的很难啃,也许弄懂之后就是一两句话的事情,但是不懂的时候一头雾水不得入门真的很难受。
(图源 :pixabay)
这两天啃了一点签名相关的内容,以ecdsa为例,简单记录一下。
STEEM/HIVE中的签名流程
首先,签名可以直接对消息签名或者对消息摘要签名,对消息签名的实质是对消息进行摘要,然后再签名。
STEEM/HIVE中的签名大致流程如下:
- 将去除签名的的transaction序列化
- 加上chainid前缀内容
- 对上述信息进行摘要(
hashlib.sha256(message).digest()
- 使用私钥对上述摘要信息进行签名
- 将签名附加进transaction并广播
因为整个过程涉及较多内容,所以我们一点一点地啃,今天只啃签名部分。
准备(摘要 & SigningKey)
为了简化代码,我们假设要处理的信息为”Hello World!”,并由此得出对应的摘要信息。
message = "Hello world!"
digest = hashlib.sha256(bytes(message, 'utf-8')).digest()
接下来,我们制定我们使用的私钥,直接用我们之前文章中生成的私钥即可(为了避免编解码,我直接用的原始的字符形式私钥)。
private_key = "415ac848c316b406920e0a4b43adc7f93c45bb89124f80ced8d1f50fae4f080d"
ecdsa中要把私钥弄成SigningKey
签名密钥:
sk = ecdsa.SigningKey.from_string(unhexlify(private_key), curve=ecdsa.SECP256k1)
#
现在可以说我们已经做好准备了,有了要被签名的摘要(digest),有了用于签名的密钥(SigningKey),接下来可以看进行签名了。
签名
首先我们来看一下SigningKey
的sign_digest
方法的接口:
def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, k=None)
其中k是作为nonce的一个随机量,这样相同的密钥相同的信息每次签名出来的结果都不相同,防御了一些猜测私钥的攻击。
但是这篇文章中为了便于对比,我把k指定为1,实际代码中千万不要这样做,好了,我们可以进行签名了:
k = 1
sigder = sk.sign_digest(digest, sigencode=ecdsa.util.sigencode_der, k=k)
print(hexlify(sigder).decode())
ecdsa.util.sigencode_der
是一个回调函数,指定签名如何编码,其中关于der编码,我找到的最直接的信息在这里 注意,这里有一些要求,我们之后文章中在详细啃:
其实呢,签名生成了两个数r、s,我们把r、s输出一下看看:
r, s = ecdsa.util.sigdecode_der(sigder, sk.curve.generator.order())
print(f"r: {r:x}")
print(f"s: {s:x}")
输出如下:
文字格式:
sigder: 3045022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022100a5d2c7770cb6bac8eaa447b1c00903ff8b9b1defd405275e56217db481d99eb1
r: 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
s: a5d2c7770cb6bac8eaa447b1c00903ff8b9b1defd405275e56217db481d99eb1
由此不难看看der就是加上了长度信息的并把r、s编码进去的二进制串。
接下来是由r, s计算出signature
signature = ecdsa.util.sigencode_string(r, s, sk.curve.generator.order())
print("signature: ", hexlify(signature).decode())
输出如下:
signature : 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a5d2c7770cb6bac8eaa447b1c00903ff8b9b1defd405275e56217db481d99eb1
由此可以看出来,其实就是上边r, s两个串链接到了一起。
ecdsa.util.sigencode_string
除了用上述代码生成signature
外,还可以在sk.sign_digest
方法中直接指定sigencode=ecdsa.util.sigencode_string
来直接生成signature
:
sig = sk.sign_digest(digest, sigencode=ecdsa.util.sigencode_string, k=k)
print("signature: ", hexlify(sig).decode())
通过对比输出,我们知道两者生成的signature
完全一样:
结论
我们用ecdsa
中的SigningKey
的sign_digest
方法就可以对消息摘要进行签名,除了输入摘要、私钥外,我们还需要提供如何编码的回调函数以及一个随机数k
。
sign_digest
方法本质是生成了r
和s
,提供不同的回调函数只是以不同的编码形式呈现,所以得出的签名是相同的。
其它
上述签名满足STEEM/HIVE的情况吗?除去一些外围的工作外,答案应该是不确定,为什么不确定呢?我们之后的文章再分析。
相关链接
This page is synchronized from the post: ‘每天进步一点点:学习用私钥签名’