在说SSH连接服务器之前先说下非对称加密RSA的理解,我也不记得我之前的博客是否有记录此理解。在查了一些SSH连接服务器的资料后发现有些人对RSA的理解有些出入,下面我先树下我自己对RSA的理解,分几点阐述:
好了,上面的理解大致就是这样,总结一下:非对称加密算法RSA有两种用法,分别是签名验签和加解密用法。然后下面来说说SSH连接服务器的原理。SSH连接服务器有两种方式,分别是通过账户密码进行连接和通过公钥进行连接。
这种方式是比较传统的方式,直接命令行“ssh user@ip”,输入密码即可连接对应ip地址的服务器。连接的时候原理如下:
如果你是第一次登录对方主机,系统会出现下面的提示:
$ ssh user@host
The authenticity of host 'host (12.18.429.21)' 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)?
这段话的意思是,无法确认host主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?所谓"公钥指纹",是指公钥长度较长(这里采用RSA算法,长达1024位),很难比对,所以对其进行MD5计算,将它变成一个128位的指纹。上例中是98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d,再进行比较,就容易多了。
很自然的一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。但是我们平时也没注意那么多,直接输入yes就直接连接了,并输入了密码。
当远程主机的公钥被接受以后,它就会被保存在文件$HOME/.ssh/known_hosts之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。每个SSH用户都有自己的known_hosts文件,此外系统也有一个这样的文件,通常是/etc/ssh/ssh_known_hosts,保存一些对所有用户都可信赖的远程主机的公钥。
SSH连接的整个过程是这样的:
(1)远程主机收到用户的登录请求,把自己的公钥发给用户。
(2)用户使用这个公钥,将登录密码加密后,发送回来。
(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。
如下图所示
从上面的流程可以看出连接过程使用的是RSA非对称算法的加解密功能,级即公钥加密,私钥解密。而且该密钥对是服务器端的密钥对,服务端将公钥给客户端,客户端使用公钥加密账户密码,然后服务器使用私钥进行解密。
这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。可以设想,如果攻击者插在用户与远程主机之间(比如在公共的wifi区域),用伪造的公钥,获取用户的登录密码。再用这个密码登录远程主机,那么SSH的安全机制就荡然无存了。这种风险就是著名的中间人攻击。后面又出现公钥的登陆方式,可以防止这种情况发生。
这种登陆的前提是客户端需要生成一对密钥,将公钥放到需访问的远程服务器。这种验证比上一种的好处是,不能仿冒真正的服务器,因为要仿冒必须拿到客户端生成的公钥。使用公钥免密登陆的大致流程如下,这里不做过多解释,因为网上的教程实在是太多了:
然后操作完成,用户就可以在命令行执行命令“ssh user@ip”连接服务器,此时就不需要输入密码了,实现了免密登陆。
能够实现公钥免密登陆,其公钥登陆的原理如下所示:前提是上面的准备工作已经完成。
(1)客户端发送连接,并将自己生成的公钥发给服务端
(2)服务器收到公钥,拿着此公钥到$HOME/.ssh/authorized_keys文件中去比对是否存在该公钥,如果有的话,就生成一段随机数挑战challenge,并使用该公钥对随机数进行加密,并把密文回给客户端;如果没有的话,直接返回,重新执行账户密码的连接流程(此处我也不是很确定是不是这样,欢迎大家评论区评判指正)
(3)客户端收到后,使用生成的私钥对密文进行解密,得到challenge的明文数据,并发给服务器
(4)服务器收到challenge的明文数据后,和自己之前生成的进行比较,如果一样,就建立连接
连接原理流程图如下所示:
从上面可以看出,公钥的免密登陆也是使用的是RSA的加解密功能,只是和区别在于,密钥对是客户端生成,客户端持有私钥,而且,客户端对应的公钥需要提前预置到服务器的$HOME/.ssh/authorized_keys文件中去。
备注1、有时候服务器没有安装SSH的server,需要在命令行进行安装
备注2、有些文件可能权限问题无法进行公钥连接,只需要配置下权限即可,网上教程也有很多
2022年2月23日更正:
使用公钥登录的时候好像应该是下面的流程,不知道到底是哪一个,记录一下,不过好像下面这个流程更安全一些:
authorized_keys
客户端尝试登录的帐户文件以获取密钥 ID。这个流程和之前的流程最大的区别是他不会将解密的随机数challenge明文发送回服务器端,而是通过和会话密钥结合生成MD5,这样又避免了数据泄露的风险,感觉这歌交互更加严谨,欢迎大家评论区讨论。
好了,之后如果有些地方理解的不对的地方再来修改。
2022年2月23日补充
后面看了更详细的SSH资料后,发现SSH是非常安全的一种远程连接,其主要还是整个传输层是加密传输的。下面我来补充下认证之前的传输加密过程。
在SSH认证之前主要存在两个大的步骤,如下
1)协议协商版本,其主要是为后面加密选择同一种加密算法等做铺垫,起主要流程如下:
如果协商成功,则进入密钥和算法协商阶段
2)密钥和算法协商,其主要是计算出共识的对称算法密钥和加密算法,其主要流程如下:
密钥协商完成后,后面的认证等所有操作都使用协商出来的密钥和算法进行加密通信,保证整个通过过程的安全,而且因为每次连接时,密钥协商算法的部分参数不一样,导致每次协商的加密密钥也不一样,这样就使得整个过程更加的安全可靠。
从上面密钥协商算法,告诉我们,SSH的客户端和服务端都含有自己的一对非对称密钥(一般是RSA),这对非对称密钥在整个算法过程中主要用来使用DH密钥交换算法计算出传输的加密密钥。这两对非对称密钥和认证的时候通过公钥登录的非对称密钥是完全两个独立的,互补干涉,各行其职。
DH密钥交换算法的粗略理解:可以这样理解:我们把SSH客户端定义为A端,SSH服务器端定义为B端。A端和B端含有自己独有的一对公私钥对,A端使用A的私钥和B端的公钥和一个随机数C,使用算法生成密钥D1;同样的道理B端使用自己的私钥和A端的公钥和一个随机数C,使用算法生成密钥D2,只要随机数C是一样的,最后生成出来的D1和D2也是一样的,后面A端和B端都使用此密钥进行加密传输。当然每次SSH连接,随机数C肯定是不一样的,所以最后的生成的D也是不一样的,这样就会更加安全。