一、什么是SSH?

SSH 为 Secure Shell 的缩写,由 IETF 的网络小组(Network Working Group)所制定;SSH 为建立在应用层基础上的安全协议。

SSH 主要由三部分组成:

传输层协议 [SSH-TRANS]

提供了服务器认证,保密性及完整性。此外它有时还提供压缩功能。 SSH-TRANS 通常运行在TCP/IP连接上,也可能用于其它可靠数据流上。 SSH-TRANS 提供了强力的加密技术、密码主机认证及完整性保护。该协议中的认证基于主机,并且该协议不执行用户认证。更高层的用户认证协议可以设计为在此协议之上。

用户认证协议 [SSH-USERAUTH]

用于向服务器提供客户端用户鉴别功能。它运行在传输层协议 SSH-TRANS 上面。当SSH-USERAUTH 开始后,它从低层协议那里接收会话标识符(从第一次密钥交换中的交换哈希H )。会话标识符唯一标识此会话并且适用于标记以证明私钥的所有权。 SSH-USERAUTH 也需要知道低层协议是否提供保密性保护。

连接协议 [SSH-CONNECT]

将多个加密隧道分成逻辑通道。它运行在用户认证协议上。它提供了交互式登录话路、远程命令执行、转发 TCP/IP 连接和转发 X11 连接。

二、SSH的2种认证方式

分别是基于账号和口令的验证方式 和 基于公钥和私钥的验证方式

在解释SSH协议之前先介绍一下非对称加密协议。在1976年以前,所有的加密都采用对称加密,既A使用某种加密规则对信息加密,B收到信息后逆向加密规则解密数据。这通信方式产生了一个难以解决的问题:A如何安全的把加密规则通知B?

在1976年有两位数学家提出了一个崭新的非对加密的概念:

1.A生成一对两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
2.B获取A生成的公钥,然后用它对信息加密。
3.A得到加密后的信息,用私钥解密。

受这个思路的启发,三位数学家Rivest、Shamir 和 Adleman 设计了一种具体实现上面描述的非对称加密的算法,以他们三个人的名字命名,就是目前在计算机领域应用非常广泛的非对称加密算法RSA加密算法。这样网络上传输的数据都经过公钥加密,然后用私钥解密,就算被第三方截获也无法解密出原始数据。想深入理解非对称加密解密的原理可以看这里

虽然非对称加密很安全很强大,但是它也有缺点,相对于对称加密它计算量更大,计算时间更长。所以在大规模数据的安全通信场景中,普遍采用非对称加密技术来交换对称加密密钥,之后的通信都采用对称加密技术加密。

三、SSH登录过程的5个阶段

1、版本号协商阶段

2、密钥和算法协商阶段

3、认证阶段

4、会话请求阶段

5、会话交互阶段

1、版本号协商阶段

服务端打开端口22,等待客户连接。

客户端向服务端发起TCP连接,连接建立后,服务端向客户端发送第一个报文,包括版本标志字符串,格式为“协议版本号 次协议版本号 软件版本号”。

客户端收到报文后,解析协议版本号,如果服务端的协议版本号比自己的低,且客户端能支持服务端的低版本,就使用服务端的协议号,否则使用自己的协议版本号。

客户端回复服务端一个报文,包含了客户端决定使用的协议版本号。

服务端比较客户端发过来的版本号,决定是否能同客户端交互。

如果协商成功,就进入密钥和算法协商阶段。否则服务端断开TCP连接。

2、密钥和算法协商阶段

服务端和客户端分别发送算法协商报文给对方,报文中包含自己支持的公钥算法列表、加密算法列表、消息验证码算法列表、压缩算法列表等。

服务端和客户端根据对方和自己支持的算法得出最终使用的算法。

服务端和客户端利用DH交换算法、主机密钥对等参数,生成会话密钥和会话ID。

​ c公 客户端公钥

​ c密 客户端密钥

​ s公 服务端公钥

​ s密 服务端密钥

在版本号协商阶段完成后:

服务端将 s公 发送给客户端。

服务端生成会话ID ,设为 id ,发送给客户端。

客户端生成会话密钥,设为 key ,并计算 res = id 异或 key。

客户端将 res 用 s公 进行加密,将结果发送给服务端。

服务端用 s密 进行解密,得到 res。

服务器计算 res 异或 id,得到 key。

至此服务端和客户端都知道了会话密钥和会话ID,以后的数据传输都使用会话密钥进行加密和解密。

3、认证阶段

3.1 基于账号和口令的验证方式:

客户端使用密钥和算法协商阶段生成的会话密钥加密账号、认证方法、口令,将结果发送给服务器。

服务端使用获得的会话密钥解密报文,得到账号和口令。

服务端对这个账号和口令进行判断,如果失败,向客户端发送认证失败报文,其中包含了可以再次认证的方法列表。

客户端从认证方法列表中选择一种方法进行再次认证。

这个过程反复进行,直到认证成功或者认证次数达到上限,服务端关闭本次TCP连接。

3.2 基于公钥和私钥的验证方式:

使用ssh-keygen程序生成公钥 id_dsa.pub 和私钥 id_dsa,一般是在客户端上生成,然后把 id_dsa.pub 通过某种方式发送给服务端。

服务端放在将要远程登录的用户目录的.ssh目录下。在Linux系统中,.ssh目录下有一个authorized_keys文件,需要将客户端的id_dsa.pub添加到这个文件中。

客户端使用密钥和算法协商阶段生成的会话密钥加密账号、认证方法、id_dsa.pub,将结果发送给服务端。

服务端使用会话密钥解密报文,得到账号、id_dsa.pub。 服务端在这个账号的目录的.ssh目录下找对应的公钥,如果没有找到,发送失败消息给客户端,如果找到,比较客户发送过来的这个公钥和找到的公钥,如果内容相同,服务端生成一个随机的字符串,简称“质询”,然后使用找到的公钥加密这个质询,然后使用会话密钥再次加密。

服务端把这个双重加密的数据发送给客户端。

客户端使用会话密钥解密报文,然后使用id_dsa再次解密数据,得到质询。

客户端利用MD5对本次会话的SessionKey和质询生成摘要Digest1,再用会话密钥加密Digest1,发送给服务端。

服务端使用会话密钥解密报文,得到Digest1,同时利用同样的摘要算法对质询和SessionKey处理得到Digest2判断两个摘要是否相同,如果不相同,发送失败消息给客户端,如果相同,认证通过。

防止中间人攻击

私钥是Server端独有的,从而保证了网络传输过程中登录数据的安全性。但是实施的过程中存在一个风险:比如攻击者插在用户与远程主机间,冒充Server端将伪造的公钥发送给用户,即可获取用户的密码登录远程主机。这就是所谓的中间人攻击。

SSH设计通过口令确认的方式避免中间人攻击。如果用户是第一次登录Server,系统会出现如下提示:

ssh xxx@xxx
The authenticity of host 'host (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.
Are you sure you want to continue connecting (yes/no)?

提示的信息主要表达的意思是:“无法确认主机xxx的真实性,其公钥指纹为xxx,是否继续登录?”由于公钥长度较长难以比对,提示信息中将其经过MD5计算转换为128位的指纹便于比较。而为确保用户能比对公钥指纹,Server需在自己的网站上贴出自己的公钥指纹用于用户核对。

若用户同意连接该Server后,系统会提示用户该host已被确认并被追加到文件known_hosts文件中以便下次登录时跳过警告。然后即可输入密码按上述流程登录。

每个SSH用户(client端)都有自己的known_hosts文件, 此外系统也有一个这样的文件,通常是/etc/ssh/ssh_known_hosts,保存一些对所有用户都可信赖的远程主机的公钥。

known_hosts中存储是已认证的远程主机host key,每个SSH Server都有一个secret, unique ID, 称为host key

参考文章:

[1] SSH2.0编程 ssh协议过程实现

[2] ssh秘钥交换详解与实现 diffie-hellman-group-exchange-sha

[3] SSH、SSL与HTTPS

[4] SSH原理简介