写作背景
最近在研究 GitHub 多账号管理 的问题,这其中就涉及到了 SSH 的连接方式,因此不得不了解一下 SSH 的工作原理。
一番搜索和实践后发现,这里面的水其实很深,真要理解透彻可能需要去读几本关于密码学的书。本人并没有多大兴趣去研究这个,所以只是浅谈,但对于只是使用 SSH 工具的人来说,这完全够用了。
几年前我了解到的是 SSH 使用非对称加密算法(RSA)来完成对称加密算法的密钥交换,最后使用对称加密算法实现数据安全传输。现在网上查到的却是先使用 Diffie-Hellman(DH)算法完成对称加密算法的密钥交换,再通过客户端的公钥识别身份。这让我丈二和尚摸不着头脑。
直到写这篇文章,在搜索了 SSH1 和 SSH2 的区别后,我才找到答案。原来这里面说的其实是两个不同的东西,一个是 SSH1,一个是 SSH2。现在都流行使用 SSH2,所以网上搜到的大多都是 SSH2 方面的东西。
如果你直接在网上搜索 “SSH 实现原理”,那么搜到的大多都只是在谈 SSH 的身份认证部分而非整一个工作原理。试着搜索 “SSH 协商会话加密”,你将会看到不一样的内容。
SSH2 连接过程
SSH2 的整一个连接过程大体上可分为两个部分:协商会话加密 和 身份认证。
这其中还用到了 对称加密、非对称加密 和 哈希 算法(MD5)。
注意 SSH2 是建立在 TCP/IP 协议之上的应用层协议,所以是在经过 TCP三次握手 后才开始进行 SSH 连接,当然这个过程也是依赖于 TCP/IP,不过这些过程对于 SSH 来说是透明的,本章也不会作这方面的说明。
一、协商会话加密
协商会话加密 使用 Diffie-Hellman 算法生成会话密钥,具体流程如下。(大概流程)
- 客户端和服务端共同选定一个大素数作为 种子值。
- 独立地,双方各自确定另一个素数,这个素数对另一方保密,简称 私钥。
- 通过 种子值 和 私钥,使用某种函数生成各自的 公钥。
- 双方交换各自的 公钥。
- 双方使用各自的 私钥、收到的 公钥 和 种子值 计算出 共享密钥。虽然双方的 私钥 和 公钥 都是不同的,但它们最后计算出来的 共享密钥 都是相同的。就这样,双方在不安全的网络中协商出了一个只有双方才知道的 共享密钥(这不得不让人惊叹数学的精妙啊),其它人即使拿到了 种子值 和 公钥,也无法确定 共享密钥,因为它们不知道 私钥,私钥 在整个过程中都没有被传输。
- 使用 共享密钥 加密随后的所有通信。
注意这里的公钥、私钥和下面要讲的非对称加密的公钥、私钥是不一样的,不是同一个东西。
二、身份认证
共享密钥 确定后,接下来的通信都使用 共享密钥 进行加密和解密,因此是安全的,但我们还没有确认双方的身份。
客户端识别服务端是通过人工进行确认的。我们在第一次连接服务器时,都会弹出一个警告,让用户确定是否进行连接。警告的内容包含了服务器的 公钥指纹,如下图。
SSH服务器验证.png既然 公钥 是公开的,那中间人是不是也可以伪造这样的信息,让我们误以为是真实的服务器发出来的?答案是“是的”,所以我们还需要确定服务端是否拥有对应的私钥。不过查阅相关的文章,好像没有这样一个过程,都是在用户确定服务端公钥(回复yes)后就进行下一步操作(确认客户端身份)。个人感觉不合理,应该是要有的,具体我也没有深究,毕竟这篇文章是 浅谈 嘛!(好像被发现为什么要用这个做标题了😁。)
其实要确定服务端是否拥有对应的私钥也很容易,接下来讲的如何确定客户端身份会讲到,道理是一样。
服务端识别客户端身份有两种方式:账号密码 和 SSH密钥对。
账号密码 验证的方式很容易理解,客户端将用户名和密码用 共享密钥 加密后发给服务端,服务端再使用 共享密钥 解密,取得用户名和密码,再以此验证用户信息。
SSH密钥对 需要管理员事先将客户端的公钥上传到服务端对应用户的 ~/.ssh/authorized_keys
文件中,再进行以下流程。
- 客户端将公钥ID发送给服务端
- 服务端在对应用户的
~/.ssh/authorized_keys
中搜索与之匹配的公钥 - 如果找到匹配的公钥,服务端将会生成一个 随机数 ,并使用该公钥加密该 随机数,得到 加密随机数
- 服务端将 加密随机数 发送给客户端
- 客户端收到 加密随机数 后,如果其持有对应的私钥,那么它就可以使用私钥解密,从而得到 随机数
- 客户端将 随机数 和 共享密钥 组合进行 MD5 加密,并将 MD5值 发送给服务端
- 服务端用同样的方式计算 MD5值 ,并与客户端发送的 MD5值 进行比较。如果相等,则身份认证通过。
前面说到在 协商会话加密 后,以后的通信都会使用 共享密钥 进行加密。在使用 SSH密钥对 进行 身份认证中,是可以不使用 共享密钥 进行加密的,因为整个过程所用到的 加密随机数 和 MD5值 是可以公开的。具体在真实的场景中有没有使用 共享密钥 ** 加密这个过程我也不太清楚(浅谈浅谈**),不过不会有什么安全问题。
问题
到这里,整个通信流程大家应该都理解了,不过我猜大家还会有一些问题。
为什么不先进行身份认证,之后再进行协商会话加密呢?
一开始我以为是为了连接速度,后来查了 SSH1 和 SSH2 的区别才发现,问题并不简单。
下面引用 海里木有鱼 的 SSH1和SSH2的区别 内容来解答这个问题。
SSH2 避免了 RSA 的专利问题,并修补了 CRC 的缺陷。
SSH2 用数字签名算法(DSA)和 Diffie-Hellman(DH)算法代替 RSA 来完成对称密钥的交换
一切,都是利益的纠葛!
本地那么多个公钥,客户端怎么知道选择哪一个?
ssh-agent 自动寻找,或者 ssh 登录时指定私钥(公钥和私钥名相同)。详细内容可以看 SSH多种远程登录方法
客户端和服务端是如何共同确定一个种子值的?
由于这个种子值是可以公开的,所以不怕被第三方知道。可以由客户端或者服务端自己随机生成然后发给对方,只要对方同意就行了。就像两个人去吃饭,讨论要选择哪个餐馆是一样的道理。
参考链接
Understanding the SSH Encryption and Connection Process,Justin Ellingwood
SSH 加密和连接过程,WqyJh
SSH1和SSH2的区别,海里木有鱼
网友评论