浅谈SSH2工作原理

封面.jpg

写作背景

最近在研究 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 算法生成会话密钥,具体流程如下。(大概流程)

  1. 客户端和服务端共同选定一个大素数作为 种子值
  2. 独立地,双方各自确定另一个素数,这个素数对另一方保密,简称 私钥
  3. 通过 种子值私钥,使用某种函数生成各自的 公钥
  4. 双方交换各自的 公钥
  5. 双方使用各自的 私钥、收到的 公钥种子值 计算出 共享密钥。虽然双方的 私钥公钥 都是不同的,但它们最后计算出来的 共享密钥 都是相同的。就这样,双方在不安全的网络中协商出了一个只有双方才知道的 共享密钥(这不得不让人惊叹数学的精妙啊),其它人即使拿到了 种子值公钥,也无法确定 共享密钥,因为它们不知道 私钥私钥 在整个过程中都没有被传输。
  6. 使用 共享密钥 加密随后的所有通信。

注意这里的公钥、私钥和下面要讲的非对称加密的公钥、私钥是不一样的,不是同一个东西。

二、身份认证

共享密钥 确定后,接下来的通信都使用 共享密钥 进行加密和解密,因此是安全的,但我们还没有确认双方的身份。

客户端识别服务端是通过人工进行确认的。我们在第一次连接服务器时,都会弹出一个警告,让用户确定是否进行连接。警告的内容包含了服务器的 公钥指纹,如下图。

SSH服务器验证.png

既然 公钥 是公开的,那中间人是不是也可以伪造这样的信息,让我们误以为是真实的服务器发出来的?答案是“是的”,所以我们还需要确定服务端是否拥有对应的私钥。不过查阅相关的文章,好像没有这样一个过程,都是在用户确定服务端公钥(回复yes)后就进行下一步操作(确认客户端身份)。个人感觉不合理,应该是要有的,具体我也没有深究,毕竟这篇文章是 浅谈 嘛!(好像被发现为什么要用这个做标题了😁。)

其实要确定服务端是否拥有对应的私钥也很容易,接下来讲的如何确定客户端身份会讲到,道理是一样。

服务端识别客户端身份有两种方式:账号密码SSH密钥对

账号密码 验证的方式很容易理解,客户端将用户名和密码用 共享密钥 加密后发给服务端,服务端再使用 共享密钥 解密,取得用户名和密码,再以此验证用户信息。

SSH密钥对 需要管理员事先将客户端的公钥上传到服务端对应用户的 ~/.ssh/authorized_keys 文件中,再进行以下流程。

  1. 客户端将公钥ID发送给服务端
  2. 服务端在对应用户的 ~/.ssh/authorized_keys 中搜索与之匹配的公钥
  3. 如果找到匹配的公钥,服务端将会生成一个 随机数 ,并使用该公钥加密该 随机数,得到 加密随机数
  4. 服务端将 加密随机数 发送给客户端
  5. 客户端收到 加密随机数 后,如果其持有对应的私钥,那么它就可以使用私钥解密,从而得到 随机数
  6. 客户端将 随机数共享密钥 组合进行 MD5 加密,并将 MD5值 发送给服务端
  7. 服务端用同样的方式计算 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的区别,海里木有鱼