SSH原理与运用(一):远程登录

作者: 阮一峰

日期: 2011年12月21日

SSH是每一台Linux电脑的标准配置。

随着Linux设备从电脑逐渐扩展到手机、外设和家用电器,SSH的使用范围也越来越广。不仅程序员离不开它,很多普通用户也每天使用。

SSH具备多种功能,可以用于很多场合。有些事情,没有它就是办不成。本文是我的学习笔记,总结和解释了SSH的常见用法,希望对大家有用。

虽然本文内容只涉及初级应用,较为简单,但是需要读者具备最基本的"Shell知识"和了解"公钥加密"的概念。如果你对它们不熟悉,我推荐先阅读《UNIX / Linux 初学者教程》《数字签名是什么?》

=======================================

SSH原理与运用

作者:阮一峰

一、什么是SSH?

简单说,SSH是一种网络协议,用于计算机之间的加密登录。

如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。

最早的时候,互联网通信都是明文通信,一旦被截获,内容就暴露无疑。1995年,芬兰学者Tatu Ylonen设计了SSH协议,将登录信息全部加密,成为互联网安全的一个基本解决方案,迅速在全世界获得推广,目前已经成为Linux系统的标准配置。

需要指出的是,SSH只是一种协议,存在多种实现,既有商业实现,也有开源实现。本文针对的实现是OpenSSH,它是自由软件,应用非常广泛。

此外,本文只讨论SSH在Linux Shell中的用法。如果要在Windows系统中使用SSH,会用到另一种软件PuTTY,这需要另文介绍。

二、最基本的用法

SSH主要用于远程登录。假定你要以用户名user,登录远程主机host,只要一条简单命令就可以了。

  $ ssh user@host

如果本地用户名与远程用户名一致,登录时可以省略用户名。

  $ ssh host

SSH的默认端口是22,也就是说,你的登录请求会送进远程主机的22端口。使用p参数,可以修改这个端口。

  $ ssh -p 2222 user@host

上面这条命令表示,ssh直接连接远程主机的2222端口。

三、中间人攻击

SSH之所以能够保证安全,原因在于它采用了公钥加密。

整个过程是这样的:(1)远程主机收到用户的登录请求,把自己的公钥发给用户。(2)用户使用这个公钥,将登录密码加密后,发送回来。(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。

可以设想,如果攻击者插在用户与远程主机之间(比如在公共的wifi区域),用伪造的公钥,获取用户的登录密码。再用这个密码登录远程主机,那么SSH的安全机制就荡然无存了。这种风险就是著名的"中间人攻击"(Man-in-the-middle attack)。

SSH协议是如何应对的呢?

四、口令登录

如果你是第一次登录对方主机,系统会出现下面的提示:

  $ 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,再进行比较,就容易多了。

很自然的一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。

假定经过风险衡量以后,用户决定接受这个远程主机的公钥。

  Are you sure you want to continue connecting (yes/no)? yes

系统会出现一句提示,表示host主机已经得到认可。

  Warning: Permanently added 'host,12.18.429.21' (RSA) to the list of known hosts.

然后,会要求输入密码。

  Password: (enter password)

如果密码正确,就可以登录了。

当远程主机的公钥被接受以后,它就会被保存在文件$HOME/.ssh/known_hosts之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。

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

五、公钥登录

使用密码登录,每次都必须输入密码,非常麻烦。好在SSH还提供了公钥登录,可以省去输入密码的步骤。

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。

这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用ssh-keygen生成一个:

  $ ssh-keygen

运行上面的命令以后,系统会出现一系列提示,可以一路回车。其中有一个问题是,要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。

运行结束以后,在$HOME/.ssh/目录下,会新生成两个文件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥。

这时再输入下面的命令,将公钥传送到远程主机host上面:

  $ ssh-copy-id user@host

好了,从此你再登录,就不需要输入密码了。

如果还是不行,就打开远程主机的/etc/ssh/sshd_config这个文件,检查下面几行前面"#"注释是否取掉。

  RSAAuthentication yes
  PubkeyAuthentication yes
  AuthorizedKeysFile .ssh/authorized_keys

然后,重启远程主机的ssh服务。

  // ubuntu系统
  service ssh restart

  // debian系统
  /etc/init.d/ssh restart

六、authorized_keys文件

远程主机将用户的公钥,保存在登录后的用户主目录的$HOME/.ssh/authorized_keys文件中。公钥就是一段字符串,只要把它追加在authorized_keys文件的末尾就行了。

这里不使用上面的ssh-copy-id命令,改用下面的命令,解释公钥的保存过程:

  $ ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

这条命令由多个语句组成,依次分解开来看:(1)"$ ssh user@host",表示登录远程主机;(2)单引号中的mkdir .ssh && cat >> .ssh/authorized_keys,表示登录后在远程shell上执行的命令:(3)"$ mkdir -p .ssh"的作用是,如果用户主目录中的.ssh目录不存在,就创建一个;(4)'cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub的作用是,将本地的公钥文件~/.ssh/id_rsa.pub,重定向追加到远程文件authorized_keys的末尾。

写入authorized_keys文件后,公钥登录的设置就完成了。

==============================================

关于shell远程登录的部分就写到这里,下一次接着介绍《远程操作和端口转发》

(完)

留言(84条)

必须:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

第一次接触ssh是git,这东西还是挺方便的。虽然我只是同步自己的小代码

chmod 700 ~/.ssh 必须

chmod 600 ~/.ssh/authorized_keys 非必须

这篇也不错,http://wowubuntu.com/ssh-tips.html,作者可以参考下,

'mkdir .ssh && cat >> .ssh/authorized_keys'
或者可以改为:
'mkdir .ssh ; cat >> .ssh/authorized_keys'

mkdir -p .ssh ,不然可能出错,后面的命令不执行。

我觉得好像有点儿不对劲啊……
远程主机上也不一定存在 authorized_keys 文件
所以,是不是应该这样:
'mkdir -p .ssh/authorized_keys && cat >> .ssh/authorized_keys'

呃,我错了……
'mkdir -p .ssh && cat >> .ssh/authorized_keys' 就好,其它的会自己处理的

ssh(ssl/tls)的原理很复杂,如果不对网络安全和数学有深入研究,很难讲得清楚。

很详细的文章
谢谢博主

用虚拟主机非常方便

能否顺便讲讲非对称加密是如何做到的?

博主的文章总是能让人看的明白。
自己知道还能讲解的让别人也知道,
我想这就是博主自身功力深厚的证明吧。

中间一段错了,公钥永远是用来加密的,私钥永远是用来解密。

不会是你看到我昨天用ssh,随便写了3个生成自己的ssh失败了,

你就以为我不会就写了这篇吧

我总怀疑你是个虚拟的人或者这文章不是你写的

自从kernel.org被爆,现在多数改为公钥认证方式,password我平时只用在内网服务器。不过像VPS,如果开password认证,从auth.log可以看到,经常有人在试你密码。
这个是比较恶心的

我参考你的这篇博客,做了个视频,谢谢
http://casts.happypeter.org/episodes/3

>>$ ssh user@host 'mkdir .ssh && cat >> .ssh/authorized_keys'

这里最好用mkdir -p .ssh,保证不管.ssh是否已经存在都正确执行。

@Todd:

谢谢指出。

我前面没有意识到,如果目录.ssh已经存在,mkdir .ssh会报错。我这就改过来,加上参数p。

用ssh-copy-id <host>木有成功,但也木有任何出错提示,完了之后ssh远端主机,还是需要输入密码。查看远端的authorized_keys文件,发现木有公钥字符串。
改用$ ssh <host> 'mkdir -p .ssh && cat >> .ssh/authorized_keys'

不知道方法一哪里出问题了。

@simenyu:

参考下面的文章,好像是说远程端的权限设置的原因。

http://superuser.com/questions/189376/ssh-copy-id-does-not-work

五、公钥登录,这部分应该写错了。
应该是:公匙是用来加密的,私匙用来解密的。

引用feynixs的发言:

五、公钥登录,这部分应该写错了。
应该是:公匙是用来加密的,私匙用来解密的。

公私钥是对等的吧,公加私解或者私加公解都只是用法问题

不错,虽然每天工作都要接触到SSH,文章提到的这些操作之前也有用过,但根本不知道原理,只是知道这样做可以达到怎么样的效果,现在看完这篇文章后清晰了很多,谢谢阮老师。

引用沙渺的发言:

公私钥是对等的吧,公加私解或者私加公解都只是用法问题

我觉得“五、公钥登录”这部分是有问题的,我认为应该是这样:

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段用用户公钥加密的随机字符串,用户用自己的私钥解密后,再发回来。远程主机把这个信息和原始信息匹配,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。(其实,用户用私钥解密之后得到原始信息X,还要用会话号P和X一起算md5发回给服务器,服务器用之前保存的会话号P和原始信息X做md5运算,和用户发回的信息匹配,如果成功,就证明用户是可信的)

引用yinzhezq的发言:

最好改为这样:'mkdir -p .ssh && cat >> .ssh/authorized_keys'

'mkdir -p .ssh && umask 066 && cat >> .ssh/authorized_keys'

解释的再清晰不过了

$ ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' 这条命令不成功,报错找不到~/.ssh/id_rsa.pub

引用南蛮虫的发言:

$ ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys'
这条命令不成功,报错找不到~/.ssh/id_rsa.pub

检查本地有没有~/.ssh/id_rsa.pub

@凯凯:

看他写的第三节「三、中间人攻击」,我估计是故意只强调客户端密钥的。

$ ssh [email protected] 'mkdir -p .ssh && touch .ssh/authorized_keys && chmod 644 .s
sh/authorized_keys && cat >> .ssh/authorized_keys'

引用feynixs的发言:

五、公钥登录,这部分应该写错了。
应该是:公匙是用来加密的,私匙用来解密的。

在"加密数据"时,是公钥加密,私钥解密;但是在"身份认证"时,是私钥加密,公钥解密,你想啊,私钥就一个,公钥可以有多个,即多个公钥对应一个私钥,加密的数据用公钥解出来了,那肯定就知对应的私钥是谁了,这就验证了身份了。

引用凯凯的发言:

我觉得“五、公钥登录”这部分是有问题的,我认为应该是这样:

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段用用户公钥加密的随机字符串,用户用自己的私钥解密后,再发回来。远程主机把这个信息和原始信息匹配,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。(其实,用户用私钥解密之后得到原始信息X,还要用会话号P和X一起算md5发回给服务器,服务器用之前保存的会话号P和原始信息X做md5运算,和用户发回的信息匹配,如果成功,就证明用户是可信的)


如果远程主机上面有多个公钥,那它选择那个公钥来对数据进行加密呢?但是如果发送的是明文,用户用私钥加密,发送到远程主机,远程主机就可以遍历公钥来对数据解密了。不要说远程主机会遍历公钥来对数据加密发送给用户啊,这明显是浪费时间的。

引用沙渺的发言:

公私钥是对等的吧,公加私解或者私加公解都只是用法问题

明显身份认证的时候私加公解划算啊。

稍稍纠正一下, centos 重启应该是 sudo /etc/init.d/sshd restart 对么?
另外补充一点跳坑信息, 如果是刚刚创建 ~/.ssh/authorized_keys 的话, 要注意该文件的权限, 可以改成如: chmod 640 ~/.ssh/authorized_keys

写得实在太好了!简洁明了!非常感谢!

写的很清楚,阮老师

感谢,
口令登陆ssh遵循公钥加密、公钥解密;win客户端登陆 ①向远端主机发送请求 ②远程主机发给win客户一个公钥(类似一个没有上锁的盒子),win客户将密码放入上锁后再次还给远程主机 ③远程主机用私钥打开公钥(盒子),验证密码一致允许登陆。
ssh公钥登陆同样遵循私钥加密、公钥解密;linux客户端先生成ssh公钥事先给远程主机更名为authorized_keys,①linux客户端向远程主机发送连接,远程主机随意发给linux客户端一串字符串 ②linux客户端用私钥加密字符串返还给远程主机 ③远程主机用事先存储的公钥进行解密,如果和事先发送的字符串一致则允许登陆。

“……middle attack)。
SSH协议是如何应对的呢?
四、口令登录”

这个“如何应对”指的是应对中间人攻击吗?

很是实用,感谢博主

对这句执行的顺序有点不明白
cat >> .ssh/authorized_keys'

楼主这一篇讲的太好了,井井有条。

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。

这部分应该是有问题的。不会使用私钥去加密,公钥是解密的。参考这里的解释,http://www.unixwiz.net/techtips/ssh-agent-forwarding.html#chal

下面这个摘录自 skypeGNU1 的BLOG 的解释就合理。


当需要连接到SSH服务器上时,客户端软件就会向服务器发出请求,请求使用客户端的密钥进行安全验证。服务器收到请求之后,先在该用户的根目录下寻找共有密钥,然后把它和发送过来的公有密钥进行比较。如果两个密钥一致,服务器就用公有的密钥加密“质询”,并把它发送给客户端软件(putty,xshell等)。客户端收到质询之后,就可以用本地的私人密钥解密再把它发送给服务器,这种方式是相当安全的。

感觉 公钥登录 完全没有解决中间人攻击的问题

下面是原文摘抄 我加了些标号
“所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。1 登录的时候,远程主机会向用户发送一段随机字符串,2 用户用自己的私钥加密后,再发回来。3 远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。”

我的观点:
如果2过程中发送的数据被中间人截取了, 中间人发送到了远程主机, 远程主机会把中间人作为可信用户 对吧? 如此如何防止中间人攻击?

引用daryl的发言:

中间一段错了,公钥永远是用来加密的,私钥永远是用来解密。

公钥和私钥本身都是既可以加密也可以解密的,公钥加密私钥解密是正常的非对称加密过程,私钥加密公钥解密应该就是数字签名的过程了。

卧槽,误导啊,公钥私钥谁加密谁解密都没弄明白,还来说ssh?

引用沙渺的发言:

公私钥是对等的吧,公加私解或者私加公解都只是用法问题

从非对称算法角度上, 有两个数e和d, 不等于d,通过公开函数f1(e,p)得到m
同样,也可以通过公开函数f2(d,m)得到p, 其中m是原始文本而p是经过变换后的文本。
一般而言成为为【编码】和【解码】, 其中持有e的人编码后的数据只有持有d的人能解码,
同样持有d的人编码后只有e能解。 这里习惯性的吧e定义为【公钥】,d定义为【私钥】
作为公钥私钥的生成着会保存【私钥】(大多数情况下私钥文件里面也会包含公钥),
然后讲【公钥】广而告之。


注意【编码】和【解码】和【加密】【解密】是有差别的。
密码学里面, 对文字进行编码后,只有特定的对象(对方)能够解码。 符合这种语义
的编解码方式称之为【加密】和【解密】。
显然用【私钥】去【编码】后的数据不能叫【加密】的,因为【公钥】所有人都知道,失去了
加密的意义。 在公钥私钥系统中, 统一采用【公钥】加密和【私钥】解密。

而且【私钥】去编码数据【公钥】再去解码数据也并不是没有意义,只是习惯性称之为【签名】
即保证这块数据由【私钥】的持有者生成而不是不让人看到数据是什么
(因为大家都有公钥都能解都能证明--比如约定解出来的数据带一个尾巴hahaha,那么所有符合xxxxhahaha的数据都可以认为是私钥发送的别人不能伪造的,发送
的有效数据是xxxx)

其中用e进行 一个公钥对应一个私钥,

引用daryl的发言:

中间一段错了,公钥永远是用来加密的,私钥永远是用来解密。

瞎扯

引用paranonia的发言:

chmod 700 ~/.ssh 必须

chmod 600 ~/.ssh/authorized_keys 非必须

谢谢!

请问这样就可以远程登录主机了吗?我租的服务器一直上不去,好奇怪。

还需要设置~/.ssh/authorized_keys的权限,否则在/var/log/secure里可以看到提示Authentication refused: bad ownership or modes for file。经测试,只有文件所有者有写权限时,才可以登录服务器。所以authorized_keys为640,600都可以登录服务器。但不能是660,620等。而执行完上面的命令,authorized_keys为660,需要修改这个文件的权限。

发现还需要设置.ssh的权限,因为.ssh目录必须700才允许登录,而mkdir -p .ssh新建的目录权限是755.

阮老师: 我有一个疑问,还望解答,谢谢.

中间人攻击那一端里面有如下一段:
整个过程是这样的:(1)远程主机收到用户的登录请求,把自己的公钥发给用户。(2)用户使用这个公钥,将登录密码加密后,发送回来。(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

经测试, 1. 远程主机 foo 用户并未生成公钥和密钥,也就是 /home/foo/.ssh/目录下并未存在 id_rsa.pub和id_rsa;
2. 本地主机 bar 用户并未生成公钥和密钥,也就是/home/bar/.ssh/目录下并未存在 id_rsa.pub和id_rsa;
3. 本地主机 bar 用户运行 ssh foo@remote_serv, 输入yes加入known_hosts后再输入密码可成功登入 remote_serv
疑问: 远程主机未生成公钥和私钥,如何发送自己的公钥给本地主机呢?

实验结果和讲解不符, 不知道是不是我理解错了呢?

发现,known_hosts里存入的不知道是什么东西,反正和对应主机的公钥不同,能详细说一下,这个中间是怎么加密的吗,用的什么加密算法

很赞的文章。之前一直没理解为什么第一次会提示“The authenticity of host 'host (12.18.429.21)' can't be established.”,感谢。

另外,在“公钥登陆”章节,您提到“登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密。” 这段话,个人有几点想交换下意见的地方:
(1)通常术语上私钥加密说成签名,公钥解密说成验签。从数学上是类似操作,但用途上签名和验签是为了证明“对方是对方”,而加密和解密是为了“发来的信息没被人偷看”,更严谨的安全会话是加密和签名同时用,因为正如您说的加密不能证明发消息的人是靠谱的。
(2)客户端与服务器是多对1的关系。“远程主机用事先储存的公钥进行解密”,如何从一堆公钥中找到当前客户端的公钥,少了一个公钥索引的点。

就我一个人很在意这个配图吗。。

window下我使用给gitbash也挺好用的

公钥登陆部分,是客户端将自己的公钥发送到服务端,服务端用客户端的公钥加密一个256位的随机字符串,客户端接收后解密,然后将这个字符串和会话标识符合并在一起,对结果应用MD5散列函数并把散列值返回给服务器,服务器进行相同的MD5散列函数,如果客户端和该值可以匹配,那么匹配成功。


望更正。

今天用github在github上保存ssh公钥,看到这里的公钥登录,明白了是怎么回事了

引用cui的发言:

公钥登陆部分,是客户端将自己的公钥发送到服务端,服务端用客户端的公钥加密一个256位的随机字符串,客户端接收后解密,然后将这个字符串和会话标识符合并在一起,对结果应用MD5散列函数并把散列值返回给服务器,服务器进行相同的MD5散列函数,如果客户端和该值可以匹配,那么匹配成功。


望更正。

很详细,确实是这样的。关于会话标识符想说的一点是客户端与服务器实际上是先有会话加密过程,再进行的客户端验证,所以这个会话标识符就是第一个阶段生成的共享密钥。


- client向server端请求(用户A想访问server系统下的账户B),该请求中包含public-key的modulus
- server读取B账户,查找是否存在匹配的public-key。如果不存在,请求失败。
- server端生成一个256 bit的字符串,用匹配的public-key加密发给client
- client端用自己的私钥解密数据,然后和当前的session identifier HASH生成md5值发给server
- server重新计算md5值,跟client进行比较

@cui:

你所说的就是md54的对称加密方法吧

五、公钥登录
这段写错了,博主赶紧改改,不要误导他人。
参见:
https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys
https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process

当然,也有可能博主是对的,这个只有撸过源码才能确定,有没有朋友有兴趣有时间找到真相的?

阮老师,关于你说的这一段我有点无法理解:
“很自然的一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。”
如果远程主机在自己网站上贴出来公钥指纹, 那么中间人也可以知道这个呀,再把假的公钥指纹发给你不就好了吗,这样你也无法判断了呀。

@榛子:

同樣是關於公鑰的問題,既然我們可以通過主機的網站來確認指紋是否正確,那主機直接在網站上公布自己的公鑰就好了,我們收到的公鑰對不對,對比網站上的公鑰就知道了。這樣想的話,指紋的存在好像是多餘的?

@榛子:

远程主机发送的,不是指纹(摘要),而是公钥本身,而指纹,是不可逆的,中间人知道了指纹,但无法推算出公钥,所以,是无法伪造公钥。

@阿布:

兄弟,文章中不是说了,公钥太长,不方便比对吗

关于公钥登录这一部分,我一开始的理解也是觉得有问题,一般我们都认为公钥加密,私钥解密,这个可能是非对称加密最常用的功能,大家可以参考下这个网页,RSA算法的作用:认证。
http://www.cnblogs.com/scofi/p/6617394.html

When a client connects to the host, wishing to use SSH key authentication, it will inform the server of this intent and will tell the server which public key to use. The server then check its authorized_keys file for the public key, generate a random string and encrypts it using the public key. This encrypted message can only be decrypted with the associated private key. The server will send this encrypted message to the client to test whether they actually have the associated private key.

Upon receipt of this message, the client will decrypt it using the private key and combine the random string that is revealed with a previously negotiated session ID. It then generates an MD5 hash of this value and transmits it back to the server. The server already had the original message and the session ID, so it can compare an MD5 hash generated by those values and determine that the client must have the private key.

说的很清楚啦这段

引用kim的发言:

...

说的很清楚啦这段

当客户端希望通过SSH KEY 鉴权登录时,它会告诉服务端,并附带上自己的公钥KEY信息。服务端检查它的KEY文件,同时生成一个随机串,并且用公钥加密。加密过的随机串,只有客户端用自己的私钥才可以解。
一旦收到消息,客户端将用自己的私钥解密,获取到随机串,同时,将先前跟服务端商定的SESSIONID一起,再次做MD5一致性HASH(md5(randomKey,sessionId),发回给服务端。服务端也按同样的方式生成摘要,如果两者一致,则验证通过。

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。

感觉还是有差异,网上也没能找到权威的解释

@kim ,能提供下出处吗?

引用沙渺的发言:

公私钥是对等的吧,公加私解或者私加公解都只是用法问题

这里讨论的这个有没有一个正确的结论?如果不确定就不要把大家误导了,ssh rsa是非对称加密吧?

@凯凯:

和其他网站将的不一样,谁能告诉我哪个说的是对的?!!

参看:https://www.cnblogs.com/hukey/p/6248468.html

阮老师,我想请问对于公钥登陆的方式,为什么就能阻止中间人攻击呢?

我做过小测试,不管口令登录还是公钥登录,第一次尝试连接的时候都会有 The authenticity of host 'host (11.22.33.44)' can't be established.这个提示,让我们判断主机是否是真实的。 如果这个IP是假的, 对于口令登录会丢失账号密码,即受到了中间人攻击;而对于公钥登录呢?我们虽然并没有丢失自己的密钥,但是也没能和真正的主机建立联系呀?

之前对ssh只看了一些简单的,这次认真重新看了一遍,感觉很多东西都变得更透彻了,中间人攻击,公钥验证,密码登录验证的原理等等,通俗易懂,大赞

感觉中间人那部分没有讲完,可能部分人还不理解,当主机的公钥追加到该主机本地文件~/.ssh/known_hosts中后,下次登陆时如果因为某些原因发生服务器ip改变(如中间人攻击),会报错Host key verification failed.这样就要再次确认ssh登陆的IP,可以有效防止中间人攻击

无意看到这片博客,感觉受益匪浅。查了查阮老师的经历,无比膜拜。感觉日后这里会是常常学习的地方啦。希望更新更多的博客:)

多谢 特别详细 之前只会一步步做 从不懂为什么

引用凯凯的发言:

我觉得“五、公钥登录”这部分是有问题的,我认为应该是这样:

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段用用户公钥加密的随机字符串,用户用自己的私钥解密后,再发回来。远程主机把这个信息和原始信息匹配,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。(其实,用户用私钥解密之后得到原始信息X,还要用会话号P和X一起算md5发回给服务器,服务器用之前保存的会话号P和原始信息X做md5运算,和用户发回的信息匹配,如果成功,就证明用户是可信的)


关于秘钥登录应该分两种情况:

第一种:客户端有私钥,没有公钥,如阮老师所讲:

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。

第二种情况,客户端有私钥,也有公钥,如kim所讲:

When a client connects to the host, wishing to use SSH key authentication, it will inform the server of this intent and will tell the server which public key to use. The server then check its authorized_keys file for the public key, generate a random string and encrypts it using the public key. This encrypted message can only be decrypted with the associated private key. The server will send this encrypted message to the client to test whether they actually have the associated private key.
...

可以在删除和不删除id_rsa.pud的情况下,分别使用ssh -v user@host进行验证。

这个帖子快十年了啊

@cui:

让我理解的话,如果公钥给服务器的话, 有点像中间人攻击,中间被拦截了。那么不就登录到其它非法远程主机上面去了吗。

@xuriwuyun:

我看了半天这个英文表述的意思跟阮老师表述的意思一样的啊。 你第二种情况怎么得到的

@凯凯:

不知道会不会有这种情况: 在这种方式下,如果远程主机发送的是一段伪随机字符串的话,非法用户疯狂的伪造随机字符串散列应答主机。这样的话,用户可信度就会存在问题。

不错的分享,一般Linux默认自带SSH只需要开启下运行,自己工作一般是windows电脑也会装上SSH客户端来远程方便。同局域网内远程是可以直接内网地址了。对于跨网,如电脑在外网SSH远程内网Linux时,可以通过内网穿透方案,如FRP或nat123,先在本地内网将SSH内网地址映射成外网域名地址,然后外网SSH客户端就可以通过域名进行远程访问。

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接