清明节:冷

昨晚风特别大,我在书房内,甚至时不时的听到大卡车驶过的轰隆隆的声音,几次之后我才知道那并不是卡车而是狂风刮过的声音。

image.png
(图源 :pixabay)

一个做餐饮的朋友,在朋友圈中发出感慨:“在龙卷风中烤串是怎样一种感觉?” 我问她那里发生龙卷风了,她说她在餐饮店门口烤串,眼看着一个大旋风从马路上一路旋过去。

我觉得她描述的这个充其量只能叫做旋风,不过后来看到微信群里朋友们分享的路边一处处手臂粗的断树枝,我才确定这风果然不小,那么我听到的轰隆隆的声音,有可能也是这种大旋风导致的。

昨天白天气温,飙升到二十三四度,加上外边阳光明媚,比屋子里要暖和的多,中午穿夹克出去一会,热得满头大汗,还被别的邻居嘲笑我穿得太多。

可是今天早晨起床,发现气温竟然骤降到0摄氏度,哪怕现在到了阳光正足的中午,气温也不过上升到4摄氏度,难怪下半夜睡觉觉得很冷很冷。

在屋里穿上毛绒绒的家居服并套上羽绒马甲再把空调加热功能打开,喝杯热咖啡,总算舒服了一点点。

为什么每年清明都是这么冷呢?尤其是清明节扫墓,无论穿多少厚衣服,每次都是冻得要死,而第二天往往有热得要死,今年清明节倒是不用去扫墓了。

image.png
(图源 :pixabay)

其实清明节也是花开草绿,是人们亲近自然、踏青游玩、享受春天乐趣的欢乐节日

可是这么冷,并且疫情还很严重,咋出去玩啊,我还是继续裹着家居服、吹着空调暖风宅在家吧。

相关链接


This page is synchronized from the post: ‘清明节:冷’

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

昨天啃了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回调的意义所所在了:

image.png

再谈公钥

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

vk = sk.get_verifying_key()

我们可以将vk打印出来:

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的打开正确方式,因为校验的时候,我们往往都是只有公钥没有私钥的

相关链接


This page is synchronized from the post: ‘每天进步一点点:学习用公钥验证’

驾驶证容缺办理

在疫情期间,换驾驶证是个麻烦事,着实不想和很多人打交道,不过驾驶证马上就要到期了,不办也不行啊,弄个无证驾驶被抓了就更惨了。


(图源 :pixabay)

不过恰巧微信群里有朋友在聊这个问题,他们都提到用交管12123这个APP来处理,这个APP我也有下,不过还真没在上边试过办证呢,打开试试吧。

进去之后,就看到有提示驾驶证要到期了,然后按提示就可以点击换证,点击换证之后提示我照片已经过期了,让我提交个新照片。

提交的新照片可以是手机里已经拍摄好的近期照片,也可以使用手机现拍摄,白色背景即可。于是我拍了一张看的顺眼的照片传了上去。

上传好之后就可以继续办理换证业务了,然后按提示填写一些住址电话之类的,提交就可以了。

可是想到没要求提交体检信息啊,确切地说我最近也没去弄过驾照这个体检,这个时候体检多危险啊。

不过群里有的朋友说,必须要体检,否则办证进度就会卡在那里,只有提交了体检信息才会继续。

于是我联系我医疗系统的朋友,问了一下体检的情况,他说体检现在也是一切从简,相对来讲是很安全的。这样我就放心了。

原本打算下周找个时间去体检,可是今天打开交管12123APP,竟然提示:你驾驶证的期满换证申请,已经通过车管所审核。还有一条“已经制作完成”的提示信息,并且给了我一个快递单号,已经寄送出来了。


(图源 :pixabay)

另外还有一条:驾驶证容缺办理通知

你存在容缺办理期满换证业务未提交身体条件证明的情形,请及时补交身体条件证明。如不及时补交,以后办理驾驶证业务将受限。

哎,看来还是免不了要去体检啊,下周再说吧。


This page is synchronized from the post: ‘驾驶证容缺办理’

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

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


(图源 :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的情况吗?除去一些外围的工作外,答案应该是不确定,为什么不确定呢?我们之后的文章再分析。

相关链接


This page is synchronized from the post: ‘每天进步一点点:学习用私钥签名’

私密日记

今天一上午帮个律师朋友处理一些很有意思的事情,本想着记录一下,不过想着涉及币圈,没准当事人还在看我的帖子,发太多细节不太好。


(图源 :pixabay)

然后就想,如果STEEM/HIVE平台有私密日记功能就好玩了,比如说我发的日记只有我自己登录后才能看到,别人看不到或者打不开。

从前端来讲,实现这个功能非常简单,比如说我可以在文章的meta里加一个标志,比如private=true,前端解析的时候遇到这个标志并且判断当前登录用户不等于author就不予显示。

不过问题是,这样内容依旧是明文的,这个平台不显示,但是其它平台可能会显示啊,所以可以用其它平台查看到,或者直接用API读取文章的方式获取。

所以要对文章内容加密才可以,咋加密呢?或许可以考虑用公钥私钥啥的?不过公钥私钥啥的咋去加密解密我还搞不懂呢。

不过如果真能实现了,或许可以有更多的玩法,比如说,仅好友可以阅读?或者说可以实现付费阅读?或者限时阅读等等。


(图源 :pixabay)

不过我也就能随便想想了,想法无限,技术有限,不得不说真的好无奈啊,还是水贴简单,不需要任何技术。


This page is synchronized from the post: ‘私密日记’

每天进步一点点:STEEM/HIVE私钥/公钥的还原

之前写了两篇关于STEEM/HIVE私钥和公钥的文章,大致搞明白了私钥和公钥是怎么弄出来的。接下来新问题来了,在STEEM/HIVE中,私钥和公钥都是编码后可阅读的方式,那么使用时又是如何还原的呢?


(图源 :pixabay)

今天我们就来一起探索一下。

私钥的还原

我们先来看私钥,我们之前得出的结论是:

STEEM/HIVE使用的私钥,就是加了前缀(0x80)和校验码并用Base58Check编码的256位二进制随机数。

我发现之前的说法逻辑有些混乱,其实应该修改为:

STEEM/HIVE使用的私钥,就是加了前缀(0x80)和校验码并用Base58编码的256位二进制随机数。而这个过程称为Base58Check

我们再来看Base58Check的编码流程:

image.png
( Source: 《Mastering Bitcoin》)

从中不难发现,其实Payload部分(红框标出部分)是没有被编码过程破坏的,那么还原的过程其实就是先用Base58解码,然后去掉version和checksum部分即可。

steem-python中代码如下:

1
2
3
4
5
6
def base58CheckDecode(s):
s = unhexlify(base58decode(s))
dec = hexlify(s[:-4]).decode('ascii')
checksum = doublesha256(dec)[:4]
assert (s[-4:] == checksum)
return dec[2:]

其中和checksum相关的两句,用于检测私钥是否合法(校验checksum),对于一个合法的私钥,这两句不影响结果。

公钥的还原

在之前公钥的文章中,我们得出如下结论:

STEEM/HIVE使用的公钥,生成方法和比特币中公钥的生成没有区别,也有压缩和非压缩两种。然后STEEM/HIVE中使用gphBase58CheckEncode对其中的压缩公钥进行编码,并加上STM前缀。

相比于私钥复杂的Base58CheckEncode,公钥的gphBase58CheckEncode要简单得多:

1
2
3
4
def gphBase58CheckEncode(s):
checksum = ripemd160(s)[:4]
result = s + hexlify(checksum).decode('ascii')
return base58encode(result)

从代码中不难分歧,其实就是使用ripemd160(payload)并截取前四个字节作为校验码,然后和payload一起并用base58编码处理。

所以,逆向的过程也并不复杂,steem-python中对应代码如下:

1
2
3
4
5
6
def gphBase58CheckDecode(s):
s = unhexlify(base58decode(s))
dec = hexlify(s[:-4]).decode('ascii')
checksum = ripemd160(dec)[:4]
assert (s[-4:] == checksum)
return dec

因为并没有两个字节的Version前缀,所以去掉checksum就可以了。同样,代码中和checksum相关的两句,用于检测公钥是否合法(校验checksum)。

对STEEM/HIVE公钥而言,另外一个地方就是公钥前边的STM前缀,这个我们可以将公钥传递给gphBase58CheckDecode之前去掉就可以了。

测试

我们继续用之前的测试代码(生成私钥和公钥),然后在把私钥和公钥还原成原始的16进制字符串形式,并与原始的私钥/公钥对比。

代码执行结果如下:

image.png

可见还原回来的私钥和公钥和原始的是一样的。

结论

私钥/公钥从可阅读的编码字符串还原成原始的字符串还是很容易的,私钥就是使用base58CheckDecode解码并去掉前缀和校验码,公钥就是使用gphBase58CheckDecode解码并去掉校验码。

相关链接


This page is synchronized from the post: ‘每天进步一点点:STEEM/HIVE私钥/公钥的还原’

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×