关于SSH

什么是SSH?
Secure Shell (安全外壳协议,简称SSH) 是一种加密的 网络传输协议,目的是实现安全远程登陆以及其它安全网络服务
SSH仅仅是一项协议标准,其具体的实现有很多,既有开源实现的OPenSSH,也有商业实现方案,使用最广泛的当然是开源实现的OpenSSH
如果一个用户使用SSH协议登录另一台远程计算机,我们就可以认为这种登录是安全的,最早的时候计算机之间的通信都是明文通信,即没有经过加密,一旦被截获信息就暴露无疑,1995年芬兰赫尔辛基理工大学的研究员Tatu Ylönen设计了ssh协议的第一个版本,同年七月份,以免费软件的形式发布了这个协议的一个实现,迅速流行起来
SSH实现原理
预备知识:密码学
SSH采用非对称加密方式,进行加密登录和传输,要进行传输首先就要 建立通信/登录 ,SSH建立连接主要有两种方式
基于口令认证
实际上是对sever端的公钥或者指纹进行确认,确认建立连接时的公钥或者指纹是属于你要连接的那个主机的,而不是黑客替换成的自己的公钥(即中间人攻击),流程如下:

远程Server收到Client端用户TopGun的登录请求,Server把自己的公钥发给用户。
Client使用这个公钥,将密码进行加密。
Client将加密的密码发送给Server端。
远程Server用自己的私钥,解密登录密码,然后验证其合法性。
若验证结果,给Client相应的响应。
可以看到,直接采用非对称加密建立连接的过程,并不安全,因为你无法确保TopGun收到的公钥来自于Sever端,比如上文提到的中间人攻击:
中间人通过这种方式可以轻易的获取你的账号密码并冒充你登录服务端,即所谓中间人攻击,那么如何避免这种情况的发生呢?就是我们所说的 基于口令的认证
第一次登陆时,系统会出现类似下面的提示信息:
1
2
3The authenticity of host 'ssh-server.example.com (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)?意思是“无法确认主机ssh-server.example.com(12.18.429.21)的真实性,不过知道它的公钥指纹,是否继续连接?”
所谓公钥指纹,其实就是将公钥进行hash生成的一个128位的序列,用这个序列就可以代表公钥,因为具有唯一性,不会重复,所以称之为指纹(fingerprint)
为什么不用公钥,反倒要生成一个指纹代替?
主要是公钥位数比较长,向rsa算法生成的公钥有1024位,比较起来不方便,由于指纹位数比较少,而且不同的公钥生成的指纹不会重复,具有唯一性,所以用指纹代替
现在又有疑问了,你怎么知道这个指纹是不是属于目标主机而不是中间人的呢?
实际上,这个没有什么好办法,远程主机必须在自己的网站上贴出自己的公钥指纹,以便用户自行核对,从而确定目标主机的身份,允许进行连接
如果输入yes后,会出现下面的信息:
1 | Warning: Permanently added 'ssh-server.example.com,12.18.429.21' (RSA) to the list of known hosts. |
即该host已被确认,并被追加到know_hosts中,然后需要输入密码,后面的流程就如第一幅图所示了

关于known_hosts文件,它记录的就是你连接过并且信赖的主机,这样当你下次再进行连接时,就不需要再次询问你是否信任该主机并进行连接了,而是直接让你输入密码进行登录
基于公钥认证
如果是根据口令进行认证,每一次登录都需要输入密码,一般登录主机都需要密码进行身份验证,确保你是主机的用户,或者其它利用ssh方式进行登录的网站,会要求你账号和密码都要输入,这样每一次登录就比较烦,都要输入账号密码才可以,但是接下来介绍的基于公钥认证就不需要这么麻烦
流程如下:

- Client将自己的公钥存放在Server上,追加在文件authorized_keys中。
- Server端接收到Client的连接请求后,会在authorized_keys中匹配到Client的公钥pubKey,并生成随机数R,用Client的公钥对该随机数进行加密得到pubKey(R)
,然后将加密后信息发送给Client。 - Client端通过私钥进行解密得到随机数R,然后对随机数R和本次会话的SessionKey利用MD5生成摘要Digest1,发送给Server端。
- Server端会也会对R和SessionKey利用同样摘要算法生成Digest2。
- Server端会最后比较Digest1和Digest2是否相同,完成认证过程。
简单说就是,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录,不再要求密码。
这样就不会有问题了,因为公钥是公开的,即使被别人截获也没有问题,因为实际上用户是通过私钥来证明自己的身份的,因为只有私钥才可以解开用公钥进行加密的随机字符串
下面是将自己的公钥设置在Github上的展示:

参考
图解SSH原理——————