洗澡时,我终于跑出来喊出了我的 Eureka

2018-12-14 13:10:06 +08:00
 ChristopherWu

来自我的公众号『 YongHao 写东西的 Cache 』 打个小广告,还是希望写的东西有人看🙊

洗澡时,我终于跑出来喊出了我的 Eureka

今天与一位帅气的同事一起解决了一下 ssh 的相关问题,在我装逼的提及不需要显式指定 identity key 的时候,顺道提及 known_hosts 也是一样的时候,说到了known_hosts的现象:

但是原理被含糊不清的略过去了———至少没有刻意提到,因为我是不大懂的。

在今晚洗澡时,回想了一下今天的事情,想到了这件事情,不禁尝试想清楚 kown_host 是啥东西,为什么需要它。

前几天看到知乎上,有一篇文章介绍了拉马努金自己尝试推导一切未知但已成结论的数学公式,作者学习此方法自己尝试推导出机器学习的论文。

不知道是不是潜意识的影响(我在那时肯定没想到这篇文章),我在结合以上已知 known_host 的两点现象,以及以前零散的 ssh 原理知识,尝试推导 ssh 为什么运作的。

RSA 的原理无需累述,就用我以前总结过的为介绍吧:

RSA 是非对称加密算法, 对称算法就是双方用同一个密钥加密。RSA 是基于对两个质数相乘容易,而将其合数 分解很难的这个特点进行的加密算法 生成公钥与私钥, 公钥加密而私钥解密, 或者相反都可以。 一般公钥公开到网上, 想发送信息给你的人用公钥加密, 而只有你拥有私钥可以解密, 这样确保了信息的保密。 或者你用私钥加密, 其他所有人都可以用公钥解密你的信息, 这样可以确保信息是由你所发出。 网上发邮件或者个人网站上所用到的签名, 就是使用此技术。

而 SSH 就是利用了这个原理,你可以从此方面尝试去推导出你如何做一个 ssh。

而我推导出的过程是这样的:

目的就是 server 要识别 client 就是 authorize_keys 里记录的 client

  1. 在 server 上的authorize_keys里添加 client 的公钥了(这步大家都知道)

  2. client 发起 ssh 连接到 server,发送公钥给 server

  3. server 用 client 发送的公钥对比 authorize_keys里的记录是否一致,认证 client 是否有权限

  4. 此时 server 发送一个字符串(如"generated by server")发送给 client,目的就是 client 用私钥加密后,发回去后,server 可以解密,与记录的字符串对比一致

  5. 此时可以确认 client 就是authorize_keys 里记录的 client 了

以上一切完成。

为什么需要known_host呢? 上述过程有一个问题就是,无法抵御中间人攻击。

假如在你以后链接 server 时,被中间人攻击了,中间人模仿 server 的行为与你进行 ssh 校验,你就会连上去,并且难以发现。

因此显然易见的一个方法就是,在第一次建立连接成功后,在一个文件里记录 IP,公钥这样的键值对,以后连接时对比一下与第一次连接的公钥是否一致即可。 而这个文件就被 ssh 命名为 known_hosts ,因此不一致时,拒绝了你的 ssh 连接,并且提示 中间人攻击。

那么保证第一次连接是对的话,就只有人工去对比了。 服务器自己公开公钥信息了,客户端自己去对比。


对比结果

事后对比,以上的做法是对的,只是没那么严谨。

有以下几个细节是不同的:

  1. 客户端应该是需要发送公钥到服务器的,目前没有看到有说明这个的地方,需要再查
  2. 发送的固定字符串,不是用明文,而是用客户端的公钥加密了
  3. 服务器发给客户端去做私钥加密时,生成的不是固定的字符串,而是随机字符串(这个是细节没有打磨,因为发送固定字符串,明显客户端私钥加密过的东西是固定的,就无法保密了)
  4. 最后对比这个随机字符串时,还用了SessionKey来做 md5,还没细查

感想

以上的文章,技术细节不怎么重要,重要的是背后的一些大家都知道道理:

对一切保持好奇心,尽量探寻其中的原理,这才对得住计算机科学。赫歇尔对好奇七色光实验为何会有额外的温度变化才发现不可见光(红外线,紫外线呢),法拉第不懈尝试各种材料才发现玻璃能帮助磁场让光改变路径,证实磁场与光有关联。 计算机也是基于黑盒上完整的生态圈,如 HTTP,TCP,CPU,PL 等,同样可以对他们保持好奇心,与用拉马努金的方法来推导构建此黑盒,与自己的对比,想必大有裨益。

花絮

Why Eurekai ? Eureka – (希腊语:εὕρηκα;拉丁化:Eureka ;词义:“我发现了!”)

阿基米德在洗澡时发现浮力原理,高兴得来不及穿上裤子,跑到街上大喊:“Eureka!”

古希腊学者阿基米德 (Archimedes),有一天,他在洗澡的时候发现,当他坐进浴盆里时有许多水溢出来,这使得他想到:溢出来的水的体积正好应该等于他身体的体积,这意味着,不规则物体的体积可以精确的被计算,这为他解决了一个棘手的问题。阿基米德想到这里,不禁高兴的从浴盆跳了出来,光着身体在城里边跑边喊叫着 “尤里卡!尤里卡!”,试图与城里的民众分享他的喜悦。

So.. Eureka! Eureka!

在自己设计想清楚了 SSH 之后,我在寒冬不禁高兴的从浴室跳了出来,光着身体在客厅边跑边喊叫着 “ Molly,Molly ”, 跟女票论述了我此番的感想。

因为她对 ssh 原理心中一直有根刺,也很感兴趣,让我把 ssh 以及 RSA 的原理都给她说了一遍,草稿如下:


3,5,7,11,13,17。。。

191 (13,17)

公钥 《=》私钥

公钥暴露,别人无法暴力破解对比出私钥

=》私钥永远不能暴露,不能发送出去,只能放自己机器。

公钥 私钥

公钥放 GitHub 服务器

molly ssh 到 GitHub =》 molly 发起 ssh 链接 -》 发公钥给 GitHub -》 GitHub 就可以跟你放 GitHub 服务器的公钥做对比,校验你有没有权限 -》“随机生成的字符串” 发给 molly -》 molly 用私钥来加密随机生成的字符串 ,发给 GitHub -》 GitHub 用 molly 的公钥来解开这个内容,对比是否刚刚发给 molly 的随机字符串 =》一切都通了。

中间人 =》 你要想像中间随便有一个人可以监听你的网络

molly -- chalres -- github

  1. SSL
  2. ~/.ssh/known_hosts
8072 次点击
所在节点    程序员
64 条回复
hxndg
2018-12-14 15:01:11 +08:00
@Keyes
那要不呢?你真的理解啥是非对称吗?
jasonyang9
2018-12-14 15:07:57 +08:00
话说 真打算花几小时来搞明白 SSH 的密钥认证机制还不如去看下源码
jasonyang9
2018-12-14 15:08:52 +08:00
@ChristopherWu #7 懒得看它一堆输出警告什么的,干脆屏蔽了
ChristopherWu
2018-12-14 15:12:18 +08:00
@jasonyang9 这操作可以。不过我还是不偷懒了-_-
chinvo
2018-12-14 15:15:07 +08:00
并没有发送公钥这一步,发送的是用你的私钥签名的登录请求,然后服务器用你的公钥验签。

而 known hosts 里面的私钥即使你用密码登录,也是会先获取服务器的公钥,因为服务器给返回的所有数据都会用服务器的私钥签名。
jadec0der
2018-12-14 15:17:24 +08:00
@ChristopherWu 所以你知道「因此显然易见的一个方法就是,在第一次建立连接成功后,在一个文件里记录 IP,公钥这样的键值对,以后连接时对比一下与第一次连接的公钥是否一致即可。」这句话里的公钥,和你文章之前出现的公钥都不是同一个公钥吗?为什么不说清楚呢
ChristopherWu
2018-12-14 15:25:43 +08:00
@jadec0der 那天事情发生后,凌晨三点多仓促而成的文章。。有很多错漏的~
Keyes
2018-12-14 15:28:55 +08:00
@hxndg
@msg7086
@ChristopherWu

几位说的没错,我对非对称具体的数学原理并不了解,我的意思是,如果在真实的应用中,私钥可以推导出公钥,那公钥是不是也能推导出私钥了?
msg7086
2018-12-14 15:29:46 +08:00
@jasonyang9 @ChristopherWu
我是
alias sss='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'

专门用来连接一次性有效的服务器,比如 Rescue CD 上临时启动的 SSH 等等,可以防止污染正常的 hosts 文件,也方便。
msg7086
2018-12-14 15:31:25 +08:00
@Keyes
密码学中的私钥推导不出公钥,公钥也推导不出私钥。
真实的应用中,私钥文件中保存了公钥和私钥的参数,所以可以「生成」出公钥(注意,不是用私钥算出公钥)。
ChristopherWu
2018-12-14 15:32:33 +08:00
@Keyes

文章是这样写的『一般公钥公开到网上, 想发送信息给你的人用公钥加密, 而只有你拥有私钥可以解密, 这样确保了信息的保密。 或者你用私钥加密, 其他所有人都可以用公钥解密你的信息, 这样可以确保信息是由你所发出』

并不是互相推导, 而是 公钥加密的, 可以用私钥来解密出内容。 反之亦然。
ChristopherWu
2018-12-14 15:34:28 +08:00
@msg7086 @asonyang9

我刚刚想了想,是有用的。我现在过几天就有一台新服务器要连,而我又不会真的去对比公钥,所以他让我输入 yes 那个步骤是没有用的。。
msg7086
2018-12-14 15:36:08 +08:00
@Keyes 另外,密码学上的私钥和公钥在地位上是可以互换的,因为他们在数学公式上本来就是对称的。
真实的应用中,为了简化操作,作为「公钥」的那一边,有一个参数是固定值( 65537 ),而作为「私钥」的那一边,同样的参数是一个随机值,这才有了私钥和公钥的区别。
如果两边的参数都是随机值,那么两边的任意一边都可以做私钥,另一边可以做公钥。
vjnjc
2018-12-14 15:38:13 +08:00
我是来看你题目里面的那个英文啥意思的。。。
ChristopherWu
2018-12-14 15:38:33 +08:00
StrictHostKeyChecking 设置为 accept-new 对我有用。

整理一下提到的 ssh 参数:
GlobalKnownHostsFile
Specifies one or more files to use for the global host key database, separated by whitespace. The default is /etc/ssh/ssh_known_hosts, /etc/ssh/ssh_known_hosts2.

UserKnownHostsFile
Specifies one or more files to use for the user host key database, separated by whitespace. The default is ~/.ssh/known_hosts, ~/.ssh/known_hosts2.

StrictHostKeyChecking
If this flag is set to yes, ssh(1) will never automatically add host keys to the ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed. This provides maximum protection against man-in-the-middle (MITM) attacks, though it can be annoying when the /etc/ssh/ssh_known_hosts file is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts.
If this flag is set to “ accept-new ” then ssh will automatically add new host keys to the user known hosts files, but will not permit connections to hosts with changed host keys. If this flag is set to “ no ” or “ off ”, ssh will automatically add new host keys to the user known hosts files and allow connections to hosts with changed hostkeys to proceed, subject to some restrictions. If this flag is set to ask (the default), new host keys will be added to the user known host files only after the user has confirmed that is what they really want to do, and ssh will refuse to connect to hosts whose host key has changed. The host keys of known hosts will be verified automatically in all cases.
Keyes
2018-12-14 15:39:52 +08:00
@msg7086 您好再请教一下,关于数字签名中的问题

我理解的,日常环境中使用非对称密钥对做消息签名是:
1、A 将 message 做 hash,得到 hash(msg)
2、用 A 的 private key 对做一次加密得到 rsa(hash(msg))
3、将 message/hash 算法 /加密过的 hash 发送给 B
4、B 用 A 公钥解密 hash,再对消息做一次 hash,看是否等于 rsa(hash(msg))
这样就完成了一次点到点的签名认证(先不提 MITM 和中立机构的事儿)

我不能确定的是 4,B 真的可以用 A 的公钥把 hash 原文解出来吗?
miyuki
2018-12-14 16:01:49 +08:00
对不起,我以为你在玩 FF14
msg7086
2018-12-14 16:05:09 +08:00
@Keyes 我们先排除掉像是 SSH 之类协议的问题,光说公钥私钥本身。
假如 priv 是私钥,pub 是公钥。
1. A 将 message 加密 rsa_encode(priv, msg) 得到密文 MM。
2. A 将 MM 发送给 B。
3. B 将 MM 解密 rsa_decode(pub, msg) 得到明文 message。

message 是什么都可以,用作验证签名的时候是 hash,但是也可以直接用来加密。

反过来如果 B 要把信息加密发给 A。
1. B 将 message 加密 rsa_encode(pub, msg) 得到密文 MM。
2. B 将 MM 发送给 A。
3. A 将 MM 解密 rsa_decode(priv, msg) 得到明文 message。
jadec0der
2018-12-14 16:08:20 +08:00
@ChristopherWu 我觉得吧,你如果没有意识到 SSH 连接中有两套公私钥,其中一套专门用来认证 known host 的话,那你这文章其实什么都没搞明白。这就是我为什么说「思而不学则殆」。

当然也可能你搞明白了只是没写出来,冒犯请见谅。
a268479513
2018-12-14 16:13:41 +08:00
@Keyes 不是,hash 跟加密是两回事,hash 的出现不是为了加密,而是为了保证消息在传输过程中没有变化,所以你说公钥能解密 hash,这个当然是不行的啊,一般如果用了 hash 加密的话,消息原文一定也会通过密钥(或者公钥)加密发送出去,接收者在接收到消息后除了要解密密文,还得解密明文,然后用明文 hash 一下看看得到的结果是不是跟密文相同,如果不同,说明消息被改过了,如果相同,则说明消息没错。hash 是这个作用,并不是加密的作用的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/517517

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX