每天进步一点点:学习用私钥签名
最近打算学习一下私钥/公钥/签名等相关内容,发现这块东西真的很难啃,也许弄懂之后就是一两句话的事情,但是不懂的时候一头雾水不得入门真的很难受。
(图源 :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的情况吗?除去一些外围的工作外,答案应该是不确定,为什么不确定呢?我们之后的文章再分析。
Hello given the quality of your article, the team of future.witness has decided to vote for you in order to encourage you to continue on this path.
You can also, if you want to help us and vote for our witness.
Here is a quick link to Steem Login, a 100% secure application developed by our team so that you can connect to all of your Steem applications.
Vote for our witness on Steem:
@futurewitness
写了还是没看懂:)