SCP免密传输和SSH登录流程详解
SCP免密传输和SSH登录协议详解
在linux下开发时,经常需要登录到其他的设备上,例如虚拟机内ubuntu、树莓派等等,经常涉及到传输文件的操作,传输文件有很多中方法,如物理磁盘拷贝,基于网络的samba服务、SCP传输、ftp文件传输等等,今天我们就来聊聊出场频率最高的SCP传输。
SCP的使用
在linux环境中,当我们需要在两台机器之间传输数据时,经常会用到SCP指令,(SCP指令其实就是基于SCP(secure copy)协议实现的应用程序),SCP的使用示例:
scp a.out downey@192.168.1.101:/home/donwey
从示例可以看出,上述示例为:将a.out这个文件传送给IP为192.168.1.101目标机器的/home/downey目录下,如果是第一次传输,终端会显示:
The authenticity of host '192.168.4.77 (192.168.4.77)' can't be established.ECDSA key fingerprint is 7f:d8:d6:37:4f:a7:6f:8d:a3:00:45:7f:0d:xx:xx:xx.Are you sure you want to continue connecting (yes/no)?
接着键入:
yes
然后系统会提示你输入账户和密码,注意这个密码指的是目标机器上用户的账号密码,正确输入之后传输完成。
知道怎么做,当然还需要知道为什么这么做,所以我们需要来探究scp指令背后的秘密。
预备知识
在介绍SCP协议之前,我们先得了解一下SSH协议,和计算机中的加密方式,因为SCP协议的登录过程基于SSH协议。
对称加密
对称加密,即在数据传输过程中对数据进行加密和解密的算法使用同一个密钥,这种加密算法有什么弊端呢?
当客户端需要与服务器端进行数据传输时,不管是由客户端还是由服务端产生的密钥,都得将这个密钥传给对方才能实现加解密,而在传输的过程中就可能造成密钥的泄漏。
那么有些人就要说了,那我不用网络传输秘钥,我可以使用口头传输或者u盘拷贝的方法。
是的,这样就可以保证安全,但是在大多数互联网应用中,通信双方是陌生设备,无法做到上述的方式。
非对称加密
针对对称加密带来的风险,非对称加密则解决了这个问题。
将加解密使用的密钥分为公钥和私钥,公钥可以公开,而私钥则由密钥生成者保存,客户端使用公钥进行加密,而服务端用私钥进行解密,且目前来说还没有出现能从公钥推算出私钥的的破解机制。
所以相对于对称加密而言,这种加密方式更加安全,但是这种加密方式的缺点是会占用更多的计算机资源,而且这种资源的耗费量是巨大的。
常见的加密方式
对称加密:
-
DES(Data Encryption Standard):加密速度较快,适合加密大量数据的场合。
-
3DES:基于DES的加密算法,顾名思义,这是对一块数据用三个不同的密钥进行三次加密,显而易见强度更高,更安全。
-
RC2和RC4:用变长密钥对大量数据进行加密,速度比DES更快
-
IDEA:(International Data Encryption Algorithm)国际数据加密算法,使用 128 位密钥提供非常强的安全性;
-
AES:高级加密标准,是下一代的加密算法标准,速度快,安全级别高,这个加密算法被广泛应用。
非对称加密:
-
RSA:是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的,目前使用最广的加密算法
-
Diff ieˉHellman:每次交换数据的时候都使用一组新的密钥,有效地防止了第三方获得私钥后解密所有信息,但是同时对中间人攻击的防护比较薄弱。
SSH协议
SSH是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境,最常见的运用是远程登录。SSH的交互流程是这样的:
-
客户端发送请求:这个请求其实就是ssh登录请求,即发起者拥有服务器上的某个用户的账号密码,以远程登录的方式来操作服务器。
-
服务器将公钥发送给客户端,这个公钥是给客户端进行数据加密用的
-
客户端收到服务器公钥,确定是否继续连接?为什么会有这么一个流程呢,既然我指定了就是要连接服务器,为什么还要让我确认一次,这会不会是多此一举呢?其实不然,这是为了防范中间人攻击。
-
客户端用公钥对密码进行加密,再发送给服务器
-
服务器用私钥对密码进行解密,返回登录结果
中间人攻击
刚刚提到中间人攻击,我们就来看看什么是中间人攻击?
我们大可以想一想,在一个不安全的网络中,如果我发送的请求被第三方截获,而第三方把他的公钥发给我,而我用他的公钥对密码进行加密,然后再发送给他,他就获得了完整的账号密码,这时候他就可以拿着从我这儿窃取来的信息登录服务器。
那么,在一个不安全的网络中,我们该怎样防范这种攻击方式呢?一般有两种方法:
-
服务器将公钥公布在官方或者以其他方式公开,让用户可以查到进行对比,如果不匹配,就在第三步的时候取消登录即可。
-
CA证书,由一个权威机构CA对公钥进行认证,CA机构的数字签名使得攻击者不能伪造和篡改证书。CA机构会为申请者发放一个公钥,CA将该公钥和申请者的身份信息绑定在一起,并为之签字。
如果一个用户想鉴别另一个证书的真伪,他就用 CA 的公钥对那个证书上的签字进行验证,一旦验证通过,该证书就被认为是有效的。
SCP的传输
说回到SCP的传输,SCP的传输第一步就是进行SSH的登录动作,然后再基于SSH协议进行文件的传输,整个传输过程是加密的。
在上面提到的ssh登录流程中,是最基本的登录流程,特点在登录时服务器要求客户端提供账号密码进行验证,其实这样是非常麻烦的,在每次登录或者传输文件时,都要输入账号密码,这对于以偷懒为终极目标的程序员来说这是不能忍受的。
SCP的免密传输
我们可以想一想,为什么在每次登录的时候都需要用户输入密码?
所有人都知道这是为了验证登录者的是否有操作权限,那换一个思路想一下,有没有另外一种方法也能验证登录者有这个登录权限呢?
当然是有的,这种验证方式建立在非对称加密协议上,当客户端C想要访问服务器S时,在客户端生成一对密钥(通常使用rsa协议),将客户端C生成的公钥放置在服务器S上,这样在连接时只需要验证服务器S上是否存在客户端C的公钥就可以完成验证操作。(详细流程见下文)
实现SCP免密传输的操作
-
如果你在服务器拥有一个账号,名为downey
-
在某一时刻你需要用一台客户机A登录到服务器上进行操作
-
在客户机A的家目录下的.ssh目录下生成一对秘钥,linux下的指令为:
ssh-keygen -t rsa -P ""
(ssh-keygen为秘钥生成应用程序,-t rsa 表示加密类型为rsa) -
执行上述命令后,系统会提示选择生成秘钥放置的目录和密码,一般为默认选择,一路回车。(但是如果你要追求更高的安全性,你可以选择添加上私钥密码),然后在/home/downey/.ssh目录下就会生成两个文件:id_rsa和id_rsa.pub,其中id_rsa为私钥,而id_rsa.pub为公钥。
-
在服务器端的/home/downey/.ssh/目录下新建文件authorized_keys(如果有则不用创建),将客户端A上刚刚生成的id_rsa.pub文件中的内容复制到服务器端 /home/downey/.ssh/authorized_keys文件中(如果里面有内容则另起一行)
这样就可以直接用scp指令(或者ssh登录)进行文件传输而不用进行账号密码验证了,相当于客户机(操作机器)与服务器建立了安全连接(原理见下文),如果换一台机器登录,依旧需要验证(因为服务器端的公钥只与这台机器上私钥相匹配)。
注意
在这些文件操作的时候,特别需要注意的就是文件的权限问题,很多朋友就是把文件从其他地方复制过来,然后修改文件确保文件内容与目标一致就可以,但是发现这样的操作并没有达到免密传输的效果,很可能就是文件权限出了问题
同时,在某些服务器平台上,可能会提供网页端的接口来存放客户端的pub_key,就像github添加公钥的操作方式,但是原理都是将客户端公钥添加到服务器中
免密传输的登录流程
实现SCP传输的第一步是通信双方进行安全连接(登录),上面提到的SSH登录流程为SSH首次登录的大概讲解,我们不妨来详细探究在免密传输时的登录流程:
-
客户端向服务端发送连接请求,询问服务器是否支持pubkey的方式进行登录
-
服务端收到客户端的请求,表示接收pubkey的方式进行登录。
-
接收到服务端的回复,客户端决定使用pubkey的方式进行登录,客户端将一段数据用私钥进行加密,生成签名,并且将自己的公钥发送给服务器。
-
服务端收到客户端发过来的数据,首先将客户端的公钥取出来,在/home/$USER/.ssh/authorized_keys/中查找是否存在客户端的公钥,如果有,进行对比。
仅仅对比是否存在客户端的公钥当然是不够安全的,服务器接着使用客户端提供的公钥对客户端发过来的签名(经私钥加密)进行解密,如果解密后的数据内容正确,表示整个验证流程完成。 -
服务端返回登录结果。
SSH登录流程疑难解答
客户端发送给服务器的签名数据
在上述免密传输的登录部分的第三点我有提到,客户端将一段数据用私钥加密生成签名,然后服务器再对这段数据进行解密,那么服务器怎么知道这段数据是否正确呢?
首先,客户端发送给服务器的数据字段是这样的:
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication
string signature //数据签名部分
而客户端使用私钥进行加密的数据字段是这样的:
string session identifier
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication
对比发现,事实上客户端同时发了一份数据和数据的签名给服务端,服务端就可以做对比了。
为什么公钥可以对私钥加密的数据解密
很多朋友看完上述免密传输登录流程之后,表示非常疑惑:
为什么公钥可以对私钥加密的数据进行解密?
公钥本来就是公开的,那岂不是私钥加密的数据根本没有安全性可言?
首先回答第一个问题:为什么公钥可以对私钥的加密数据进行解密。答案是:这是SSH协议的规定(好像是废话)。
好吧,正经地回答第二个问题,公钥是公开的,所有人都可以获得,而私钥只有拥有者才有,那由这对密钥加密的数据是否就没有安全性可言呢?
答案当然不是,安全性高着呢。只是,一般的加密过程是公钥加密,私钥解密,这部分数据自然是安全的。
那么私钥加密的数据呢,私钥加密的数据确实没有安全性可言,因为用私钥加密数据根本就不是为了安全性,而是作为一种验证身份的手段。
因为私钥是唯一的,一旦使用公钥对私钥加密的数据解密成功,服务端S就可以完全确定这条消息是由拥有对应私钥的客户端C发出的,而不是第三方伪装者发出的。
所以,公钥加密私钥解密,是为了数据的安全性,而私钥加密公钥解密,则是为了验证数据发送者的身份。
数据传输的加密方式
一般情况下SSH使用rsa加密算法登录,SCP基于SSH协议,所以很多人自然地认为在数据传输时,使用的加密算法也是rsa。
事实上,由于非对称加密算法计算太过于耗时,对所有数据进行加密根本不现实,所以在传输数据时一般使用对称加密算法。
上面有提到,对称加密算法的弊端是密钥传输过程容易被窃取,所以通常使用非对称加密算法来传输对称加密算法的密钥,来保证安全性。
为此,我还使用wareshark工具试图梳理整个数据传输流程,花了一天的时间得出一个结论:针对整个SCP实现的加密算法这一块,以博主的水平,暂时惹不起....(不过也只是暂时!!)
不过收获还是有的:SCP数据传输流程结合了多种加密算法,同时还根据不同级别的安全要求结合不同的加密算法。
一般来说,综合对称和非对称加密算法的特性,非对称加密一般用于登录验证、密钥传输等数据量小、安全性要求较高的的部分。
而对称算法用来传输大量数据。
网上的错误描述
事实上,网络上对于SSH的登录过程描述有不少错误之处,最经典的一个错误观点是服务器发送一个通过公钥加密的字符串给客户端,客户端私钥进行解密,然后发给服务端。
服务端将发出的字符串与收到的字符串进行对于,完成验证过程。
这种做法看起来是非常合理的,但事实上SSH并不采用这种做法,具体原因引用官方文档中的一句话:
the signing operation involves some expensive computation.
To avoid unnecessary processing and user interaction。
这句话意思很简单,加密的计算操作是非常耗时的,我们应该避免不必要的交互以节省资源。
为什么我那么自信确定它们的描述有误呢?并不是飘柔给我的自信,而是官方文档
(多嘴一句,找资源一定要先找官方文档的)
github的免密传输
用过github的盆友可能对免密传输有种熟悉的感觉,没错!在github的配置中,如果需要进行免密传输,流程是:
-
在客户机生成一对秘钥,同样是id_rsa,id_rsa.pub
-
将id_rsa.pub里的内容添加到github的settings的SSH and GPG keys选项页面中
同样的,相当于github服务端与机器已经建立用户识别机制,不需要再用账号密码来识别。
好了,关于SCP传输的讨论就到此为止啦,如果朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言
原创博客,转载请注明出处!