Android之网络—第二篇(Https原理)

前言:这篇网络系列的初衷是分享下网络相关的知识,文章属于个人的学习总结博客。部分内容来源网络,如有不适,请私聊。

Android之网络—第一篇(Http原理)
Android之网络—第二篇(Https原理)
Android之网络—第三篇(解读OkHttp)
Android之网络—第四篇(解读Retrofit)

什么是Https:

说的通俗一点就是身披安全衣的Http,本质还是http,只是在http外层嵌套了一个SSL/TLS的安全层,该层做了一些数据的加解密处理。

在讲解Https原理之前,先做点准备工作,因为会涉及到SSL/TLS连接建立、SSL/TLS加解密方面的知识。所以会整体从网络架构和比较重要的知识点回顾下网络知识。

TCP/IP协议

在讲解什么是SSL/TLS之前,回顾下TCP/IP协议的分层概念。通常一个网络的传输中间会经过很多的传输节点,才最终达到服务器。期间过程会包含数据的拆分和拼装、IP的解析、数据的传输等等操作,但是网络传输是很不稳定的,如果这次网络请求在中间的某一节点失败了,难道还要重新再发送一遍么?答案是不应该这么做。

Android之网络—第二篇(Https原理)_第1张图片
image.png

为了网络传输的统一规范,就设计了这么一套网络通信的规范,每一层都专注做一件事情,即使当前失败了,也在这层做处理就可以了,尽量避免重发。


Android之网络—第二篇(Https原理)_第2张图片
image.png

简单解释下,每层的含义:

  • 应用层:决定了向用户提供应用服务时候的通信活动。应用层负责传送各种最终形态的数据,是直接与用户打交道的层,典型协议是HTTP、FTP等。
  • 传输层:负责传送文本数据。传输层有两个性质不同的协议: TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。
  • 网络层:负责分配地址和传送二进制数据,主要协议是IP协议;
  • 链路层:负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网、ADSL等。

通过上图发现,数据是由上往下传递后,再由下往回传递。这是怎么回事呢?总结就是:在 TCP / IP 协议中数据先由上往下将数据装包,然后由下往上拆包。在装包的时候,每一层都会增加一些信息用于传输,这部分信息就叫报头,当上层的数据到达本层的时候,会将数据加上本层的报头打包在一起,继续往下传递。在拆包的时候,每一层将本层需要的报头读取后,就将剩下的数据往上传。


Android之网络—第二篇(Https原理)_第3张图片
image.png

简要分析下传输过程:

  • 首先作为发送端的客户端在应用层(HTTP 协议)发出的HTTP请求(如:想浏览www.baidu.com),并生成HTTP报文。
  • 为了传输方便,在传输层(TCP 协议)把从应用层处收到的数据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
  • 在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链路层。
  • 给这些数据附加上以太网首部并进行发送处理,生成的以太网数据包将通过物理层传输给接收端。
  • 接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP 请求。

TCP的三次握手和四次挥手

这里简单总结下传输层的两种连接方式:TCP和UDP

  • 用户数据报协议(UDP):这种类型不要求建立和断开连接,发送端可任何时候发送数据,接收端也不知道自己何时从哪里接受数据。
  • 传输控制协议(TCP):发送数据之前,需要在收发主机之间建立一条通信线路,在通信传输前后,专门进行建立和断开连接的处理,如果与对端之间无法通信,可避免发送无谓的数据。

三次握手:客户端主动打开连接,服务器被动打开连接。

  • Android之网络—第二篇(Https原理)_第4张图片
    image.png
  • 第一次握手:客户端向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。

  • 第二次握手:服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。

  • 第三次握手:客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。服务器端检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

为什么3次握手:
  • 前两次的握手很显然是必须的,主要是最后一次,即客户端收到服务端发来的确认后为什么还要想服务端再发送一次确认呢?

  • 考虑如下的情况:客户端发送了一个连接请求报文段到服务端,但是在某些网络节点上长时间滞留了,而后客户端又超时重发了一个连接请求报文段该服务端,而后正常建立连接,数据传输完毕,并释放了连接。如果这时候第一次发送的请求报文段延迟了一段时间后,又到了服务端,很显然,这本是一个早已失效的报文段,但是服务端收到后会误以为客户端又发出了一次连接请求,于是向客户端发出确认报文段,并同意建立连接。但是客户端认为之前的连接已经失效了,不会给服务器发送任何数据,服务端就会一直等待下去,直到超出保活计数器的设定值,而将客户端判定为出了问题,才会关闭这个连接。这样就浪费了很多服务器的资源。

  • 如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

四次挥手:客户端主动关闭,服务器被动关闭

  • Android之网络—第二篇(Https原理)_第5张图片
    image.png
  • 第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。

  • 第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。意思是说“你的请求我收到了,但是我还没准备好,请继续你等我的消息。”
    客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

  • 第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。意思是说“好了,我数据发送完毕了,你可以关闭了。”

  • 第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态,如果服务器端没有收到ACK则可以重传。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。而服务器只要收到了客户端发出的确认,立即进入CLOSED状态。

为什么客户端最后还要等待2MSL:
  • 第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。

  • 第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样使下一个新的连接中不会出现旧连接的请求报文。

名词解释:
  • ACK:TCP报头的控制位之一,对数据进行确认.确认由目的端发出,用它来告诉发送端这个序列号之前的数据段都收到了.比如,确认号为X,则表示前X-1个数据段都收到了,只有当ACK=1时,确认号才有效,当ACK=0时,确认号无效,这时会要求重传数据,保证数据的完整性.
  • SYN:同步序列号,TCP建立连接时将这个位置1。
  • FIN:发送端完成发送任务位,当TCP完成数据传输需要断开时,提出断开连接的一方将置1

现代密码学

说个概念性的东西就是,现代密码学分为对称加密和非对称加密,跟传统密码学不一样的地方就是,除了可以加密文字内容外,还可以用于各种二进制数据的加解密。

对称加密:

通信双方使用同一个密钥,使用加密算法配合上密钥来加密,解密时使用解密算法(加密过程的完全逆运算)配合密钥来进行解密。常用的经典算法:DES(56 位密钥,密钥太短而逐渐被弃用)、AES(128 位、192 位、256 位密钥,现在最流行)。


Android之网络—第二篇(Https原理)_第6张图片
image.png
非对称加密:

通信双方使用公钥和加密算法对数据进行加密得到密文;使用私钥和加密算法对数据进行解密得到原数据。常用的经典算法:RSA(可用于加密和签名)、DSA(仅用于签名,但速度更快)。


Android之网络—第二篇(Https原理)_第7张图片
image.png

这个有什么用?其实这个就是后面Https加解密的原理。原理这么简单么?是的,就这么简单。但是要理解还得慢慢往下看。

  • 第一个问题:这些对称加密和非对称加密的破解都有哪些?

暴力破解法(穷举法):对于对称加密而言,我只要拿到密钥就可以破解了。同样的,非对称加密只要拿到私钥也就搞定了。所以只要拿一堆密码一个个的试,在最短的时间内找到对应的密钥/私钥就搞定了。

而反破解,或者对一个优秀加密算法的定义标准是,让破解者找不到比穷举法(暴力力破解法)更有效的破解手段,并且穷举法的破解时间足够长(例如数千年)

  • 第二个问题:这些对称加密和非对称加密的优缺点?
  • 对称加密:不能在不安全网络上传输密钥,一旦密钥泄露则加密通信失败。
    正常的网络传输是要经过很多个节点的,为了保证通信的双方正常通信,都必须要求知道密钥,但是密钥如果在网络传输中就很容易被劫持。有泄漏的风险。如下场景:在网络传输的过程中在某个节点被C劫持,C就可以解密你传输的内容啦。


    Android之网络—第二篇(Https原理)_第8张图片
    image.png
  • 非对称加密:可以在不安全网络上传输密钥(准确来说是公钥),但是计算复杂,因此性能比对称加密差很多。
    在正常的网络上通信,为保证正常通信,通信的双方都需要持有对方的公钥将数据加密后发送给对方,接收方再将加密的数据通过自己的私钥解密出来。这样即使在传输的某个节点被C劫持了,C也解密不了你的数据,因为数据只能用私钥来解。

    Android之网络—第二篇(Https原理)_第9张图片
    image.png

    \color{red}{但是,这里会有个问题,C解不了数据,但是可以改数据呀。可以冒充给你传输假数据啊。咋办?}

  • 第三个问题:私钥加密的数据,用公钥来解能解么?

根据上面的场景,A用B的公钥加密的数据发送给B,B用私钥可以解。反过来?B用私钥加密的数据发送给A,A用B的公钥能不能解?答案是可以的。下面举个不规范的例子说明下:

有个数字库:0123456789包含十个数字,加密算法都定义为:两数相加,满十时取个位。

正常逻辑:A 有原数据为110,使用B的公钥4和加法算法后的密文为554;然后B用自己的私钥6和加密算法解密后的数据为110.


Android之网络—第二篇(Https原理)_第10张图片
image.png

反向逻辑:B有原数据110,使用自己的私钥和加密算法加密后的密文为776;然后A用B的公钥4和加密算法解密后的数据同样为110.


Android之网络—第二篇(Https原理)_第11张图片
image.png

结论就是:公钥加密的数据用私钥可以解,私钥加密的数据,用公钥也可以解。但是要注意的是,公钥和私钥是不能对调的。

\color{red}{这个结论有什么用呢?可以用来做数字签名,验证数据信息的来源。也就是刚才的冒充数据行为解决方法。}

签名和验证:

A用自己的私钥通过加密算法得到的数据密文数据,这个数据就可以称为签过名。接收方B再用A提供的公钥通过加密算法就可以还原数据,从而就验证了数据的真实性。因为只有A一个人拥有自己的私钥。


Android之网络—第二篇(Https原理)_第12张图片
image.png

到这里咱们就可以聊聊刚才中间人伪造数据是如何处理了。通过对称加密可以防止中间人偷窥数据,通过数字签名可以防止中间人篡改伪造数据。


Android之网络—第二篇(Https原理)_第13张图片
image.png

但是完整版的签名信息需要将签名数据取Hash值,减少数据大小


Android之网络—第二篇(Https原理)_第14张图片
image.png

Https的加密原理分析

好了,看完理解了上面的知识点,到这里我们可以慢慢分析Https是如何工作的了。

先来总结一句话:Https的本质就是在客户端和服务端之间用非对称加密协商出一套对称密钥,每次发送信息之前将内容加密,接收后解密,达到内容的加密传输。解释下,就是整个数据的传输过程是用对称加密的方式来传输的,只是密钥的生成是由客户端和服务端在创建连接的时候通过非对称加密的方式协商生成的。

那这里就会有一系列的问题啦:
Q:为什么不直接用非对称加密的方式直接加密呢?
A:因为非对称加密的计算过程是复杂的数学运算,太复杂了,很慢。
Q:哦哦,那既然使用对称加密的话,这个对称密钥是怎么来的?
A:在实际的场景中,服务端会对接N个客户端,这个对称密钥如果都使用同一个密钥来通信的话,肯定是不合理的,只要破解了其中一个,其他所有的都会被破解。所以整体的网络架构应该是使用不同加密方式用不同的密钥来进行数据传输的。模型如下图


Android之网络—第二篇(Https原理)_第15张图片
image.png

Q:但是在网络场景中,对称加密的密钥是不能直接在网络上传输的。服务端和客户端是如何都知道的呢?
A:这个是服务端和客户端一起协商根据只有它俩知道的规则分别生成,就不用通过网络传输啦。
Q:如果保证它俩协商出来的密钥不被破解呢?
A:当然是使用非对称加密的方式啦。通过之前的加密知识,可以知道非对称加密是目前来说是绝对安全的。而且一个私钥可以有多个公钥,正好满足一个服务端对N个客户端的场景。模型如下图:


Android之网络—第二篇(Https原理)_第16张图片
image.png

实际在协商通讯的过程中,这个公钥是服务端给客户端发送的。而且需要注意这个公钥是用来协商生成对称密钥的,不是用来做数据的加密传输的

Q:哦哦,原来是这样,但是这样子还是有问题啊,客户端与服务端在协商生成密钥的过程中为了保证数据被偷窥和被篡改的风险,一般会要求有两套公钥和私钥分别做加解密和签名验证处理的,上面的模型只有一套公钥和私钥,没法规避数据被被篡改的风险呀。
A:能提出这个问题,说明之前的学习理解得很好。从上面的模型,可以保证在协商的过程中客户端A/B/C/D分别向服务器传输的数据是安全。但是服务器发送给客户端的数据是如何保证的呢?换句话说就是,客户端如何验证数据是服务端发送过来的,而不是被中间假冒掉包的数据。这不就是之前讲的数字签名的内容么?而实际情况中,就是通过CA证书来处理这个问题的。
Q:那这个CA证书是怎么验证的呢?
A:请看下面的CA证书的分析。

CA证书链分析。

回归之前的分析,我们的问题点卡在了“服务器发送给客户端的数据是如何保证的呢”。对吧。实际上在协商通信的过程中,服务端会先给客户端下发证书信息,这个证书信息里面会包含非对称加密的公钥。但是考虑一个问题,如何保证这个公钥就是客户端要的公钥呢?或者说怎么保证这个证书就是真实的证书,而不是被篡改假冒的证书。只有验证了这一步,客户端才完全信赖服务端,才给服务器发消息,也才接受服务器的消息。这时候就只需要一套公钥和私钥客户端和服务端就可以通信了。

Q:那客户端如何验证服务端下发的证书和公钥是正确的呢?
A:先换个概念,将证书里面的公钥假设为一串数据。要验证这个数据,只需要提供这串数据的签名以及加密这段数据的签名的私钥对应的公钥就可以了,如下图框框所示。


Android之网络—第二篇(Https原理)_第17张图片
image.png
image.png

Q:那这样子又有另外的一个问题产生了怎么去验证这段数据的签名的私钥对应的公钥了?
A:同样的,也是需要提供对应的签名和对应签名的私钥对应的公钥。

Android之网络—第二篇(Https原理)_第18张图片
image.png

Q:但是这样子下去就会变成一个循环了,怎么办?
其实在这里还会有一个场景问题:第三方签发机构不可能只给你一家公司制作证书,它也可能会给中间人这样有坏心思的公司发放证书。这样的,中间人就有机会对你的证书进行调包,客户端在这种情况下是无法分辨出是接收的是你的证书,还是中间人的。因为不论中间人,还是你的证书,都能使用第三方签发机构的公钥进行解密。

A:是的,为处理这个问题,就需要讲一个根证书的东西。先举个例子:假设你是HR,你手上拿到候选人的学历证书,证书上写了持证人,颁发机构,颁发时间等等,同时证书上,还写有一个最重要的:证书编号!我们怎么鉴别这张证书是的真伪呢?只要拿着这个证书编号上相关机构去查,如果证书上的持证人与现实的这个候选人一致,同时证书编号也能对应上,那么就说明这个证书是真实的。同样的,Https请求验证时,会用到手机操作系统内置的根证书去验证这个证书的真伪的。

到这里就简要分析完了证书的验证过程。证书会包含很多信息,包括服务器公钥,服务器名字,服务器地区等等信息。这个是没法篡改的。其中对Https连接来说最重要的就是服务器的公钥。


Android之网络—第二篇(Https原理)_第19张图片
image.png
Https连接

之前学习完TCP的连接过程,现在我们开始来唠唠Https连接。什么是Https连接呢?准确来说就是SSL/TLS加解密层的连接。

大致的建立流程:

  • 1、客户端请求建立TLS连接
  • 2、服务端发回证书
  • 3、客户端验证服务端证书
  • 4、客户端信任服务器后与服务端协商对称密钥
  • 5、使用对称密钥开始通信

详细的建立流程:


Android之网络—第二篇(Https原理)_第20张图片
image.png
  • 1、客户端给服务器发送一个Client Hello。同时会还会发送一些附加信息,客户端支持的可选TLS的版本信息,比如支持1.0/1.1;Cipher Suite(加密套件):客户端支持的对称加密算法有哪些,比如AES/DES。支持的非对称加密算法有哪些,比如RAS和支持的Hash算法等等加密信息;同时还会有一个客户端的随机数。

  • 2、服务端收到信息后,会给客户端一个回应Server Hello。同时也会发送一些附加信息。服务端选定TLS的版本,比如1.1;选定的Cipher Suite(加密套件):对称加密用AES,非对称加密用RAS等等选定的加密方式;同时还会有一个服务端的随机数。
    接着,服务器给客户端下发证书。

  • 3、客户端验证该证书信息,拿到证书内的公钥。
    注意,在验证这一步的时候,可以区分证书是否是被劫持假冒的证书。如果是假冒的,验证不通过,https链接失败。

  • 4、客户端产生一个随机数pre-master secret并加密后发送给服务器。这时候客户端和服务端就可以计算对称密钥了。
    注意,即使劫持方拦截后,还将正确的证书下发给客户端,客户端验证通过后,加密后的随机数发送给服务器被劫持后,也只有服务器的私钥能解,不怕信息泄露和篡改。即使劫持篡改了,后续生成对应的对称密钥是不一致的。在第五步,互相发送验证消息时是验证不通过的。https链接也失败。
    将客户端随机数和服务端随机数及pre-master secret通过算法计算出对称密钥Master secret。其实这个Master secret包含的信息主要有四个:客户端加密密钥,服务端加密密钥,客户端MAC secret,服务端MAC secret。具体的使用是:客户端发送加密数据时,服务端用客户端密钥解密;服务端发送加密数据时,客户端用服务端密钥解密。

为啥要这么处理呢?主要是防止消息被扔回来。如果只有一个密钥的话,场景是:A:“分手吧” 。期待B看到后有回应“别分手”。但是中间劫持后,虽然看不懂,但是直接扔回来。然后A解密看到就是“分手吧”。那就凉凉了

客户端MAC secret,服务端MAC secret的主要作用:用来认证这个消息是正确的,完整的,解决对称加密方式没法验证消息的缺点。

  • 5、客户端和服务端相互发送验证信息(不过是客户端先发送)

至此完整的Https在TLS层连接过程分析完毕。

后序:关于Https原理篇主要涉及到的知识点比较多,主要是请求连接的过程及数据的加解密模块。后续文章会针对原理的内容从源码角度去解读OkHttp和Retrofit两个官方开源的网络库。

如果觉得我的文章对你有帮助,请随意赞赏。您的支持将鼓励我继续创作!

你可能感兴趣的:(Android之网络—第二篇(Https原理))