从 croc 库中学到一个新名词 Pake
这些天看了一下 croc 库的实现。这是一个用来传输文件的工具,用处和 sftp, sz/rz 这些东西类似。但是不同之处在于这个工具文件的收发需要依赖于外部的一个 relay server。
- sender 先与 relay server 建立连接,生成一个随机的 secret,注册房间信息 room。
- sender 通过某种方式将 secret 发送给 receiver。(邮件,微信,😓)
- receiver 带上这个 secret 连接 relay server。找到对应的房间信息。
- relay Server 将这两个 TCP 连接通过管道 pipe 连接,让他们相互通信。
- sender 将文件发送给 receiver。
当然这种信息传递方式没有加密的话是非常危险的,尤其是在还有一个 relay server 存在的情况下。
- 在 sender 与 relay server 建立连接注册 room 之前。
- 在 receiver 与 relay server 建立连接获取 romm 之前。
- sender 和 receiver 连接 pipe 之后,传递文件数据之前。
在上述 3 个时间点连接的双方会使用一种叫做 Pake (Password-Based Authenticated Key Establishment Protocol) 的协议协商加解密的密钥。中文叫做 口令认证密钥交换协议。
Pake
具体的原理可以参看 一个酷炫却不普及的协议——PAKE,我对于这种数学算法比较头疼,就不解释了。
下面写个代码例子看一下。
// 这既是公钥。必须双方一开始约定好。
weakPassword := "foo-bar"
// P 为 sender,Q 为 receiver。
// P 和 Q 都使用同样的 weakPassword 初始化。
P, err := pake.InitCurve([]byte(weakPassword), 0, "siec")
if err != nil {
panic(err)
}
Q, err := pake.InitCurve([]byte(weakPassword), 1, "siec")
if err != nil {
panic(err)
}
// P -> Q
// P 将自身信息发送给 Q,所以 Q 更新。
if err := Q.Update(P.Bytes()); err != nil {
panic(err)
}
// Q -> P
// Q 再将自身信息发送给 P,P 更新。
if err := P.Update(Q.Bytes()); err != nil {
panic(err)
}
// 得到双方的 sessionKey,即协商密钥。
KeyP, err := P.SessionKey()
if err != nil {
panic(err)
}
KeyQ, err := Q.SessionKey()
if err != nil {
panic(err)
}
// 可以发现是相同的。
fmt.Printf("Key of P: %s\n", hex.EncodeToString(KeyP))
fmt.Printf("Key of Q: %s\n", hex.EncodeToString(KeyQ))
得到打印:
15:26 test ➜ go run main.go
Key of P: cebe3dbfc2afb1579e4649726e8ce352de3dc59ce5ca7ab31bebaf5b0aa36a9c
Key of Q: cebe3dbfc2afb1579e4649726e8ce352de3dc59ce5ca7ab31bebaf5b0aa36a9c
你可能得到的打印与我不一样,很正常。每次打印都是有随机数影响的,都是不一样的。
这样双方就能用这个密钥进行对称加密通信传输数据了。中间人只能截获到中间数据,无法推算出最后的密钥。即使知道了一开始的公钥,也没用。
如果双方一开始使用的 weakPassword 就不相同的话。在
P.Update(Q.Bytes())
处就会报错了。
posted on 2021-03-25 15:44 alfred_zhong 阅读(490) 评论(0) 编辑 收藏 举报