每天进步一点点:学习用私钥签名

in #cn4 years ago

最近打算学习一下私钥/公钥/签名等相关内容,发现这块东西真的很难啃,也许弄懂之后就是一两句话的事情,但是不懂的时候一头雾水不得入门真的很难受。


(图源 :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),接下来可以看进行签名了。

签名

首先我们来看一下SigningKeysign_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编码,我找到的最直接的信息在这里 注意,这里有一些要求,我们之后文章中在详细啃:

image.png

其实呢,签名生成了两个数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}")

输出如下:

image.png

文字格式:

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完全一样:

image.png

结论

我们用ecdsa中的SigningKeysign_digest方法就可以对消息摘要进行签名,除了输入摘要、私钥外,我们还需要提供如何编码的回调函数以及一个随机数k

sign_digest方法本质是生成了rs,提供不同的回调函数只是以不同的编码形式呈现,所以得出的签名是相同的。

其它

上述签名满足STEEM/HIVE的情况吗?除去一些外围的工作外,答案应该是不确定,为什么不确定呢?我们之后的文章再分析。

相关链接

Sort:  

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

写了还是没看懂:)

Coin Marketplace

STEEM 0.18
TRX 0.14
JST 0.030
BTC 60115.56
ETH 3203.28
USDT 1.00
SBD 2.46