通常使用的网络(包括互联网)是在TCP/IP协议族的基础上运作的。而HTTP属于它内部的一个子集。
把与互联网相关联的协议集合起来总称为TCP/IP。
TCP/IP协议族里重要的一点就是分层。TCP/IP协议族按层次分别分为以下4层:应用层、传输层、网络层和数据链路层。
应用层决定了向用户提供应用服务时通信的活动。
TCP/IP协议族内预存了各类通用的应用服务。比如,FTP(File Transfer Protocol,文件传输协议)和DNS(Domain Name System,域名系统)服务就是其中两类。
HTTP协议也处于该层。
传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输。
在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和UDP(User Data Protocol,用户数据报协议)。
网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方。
用来处理连接网络的硬件部分。硬件上的范畴均在链路层的作用范围之内。
TCP/IP为什么需要分层?最主要的原因是现实网络的不可靠性。
在应用层使用HTTP协议传输,如果数据过大传输失败,就需要将整个数据重新发送一次。
比如在应用层发送 abcdefghijklmnopqrst 这一块数据,这一整个数据通过应用层HTTP协议发送,如果失败了就要全部重发成本太大。
为了解决这个问题,另一种方案就是将数据分块传输。
还是同样的数据,假设分块后的数据被切割为:
1、abcdef
2、ghijkl
3、mnop
4、qrst
将数据切成四份,假设1、2、4传输成功返回了响应,3失败了,只需要将3重新传输一次即可,这样最终发送的成本为4/5,因为3发送了两次,相比整块的传输成本是2倍要少一些。
应用层不仅有HTTP协议,还有其他应用层协议,为了能够一起公用分块传输的能力,所以就需要分层抽离出来公用,也就有了传输层,由传输层负责将数据进行切割和组装。
同样的,传输层不仅只有TCP协议,还有UDP协议,它们都需要网络,所以需要再分层,将IP协议分层到网络层。
需要注意的是,传输层分块的数据在网络层是不知道组装起来是一整块数据的,网络层只会收到数据就传,最终到达对方的传输层接收数据时才会知道分块的数据是一整块的数据。
最后一层是数据链路层,也可以理解为我们的以太网、wifi这些。
发送端的客户端在应用层(HTTP协议)发出一个想看某个Web页面的HTTP请求。
接着,为了传输方便,在传输层(TCP协议)把从应用层处收到的数据(HTTP请求报文)进行分割,并在各个报文打上标记序号及端口号后转发给网络层。
在网络层(IP协议),增加作为通信目的地的MAC地址后转发给链路层。这样一来,发往网络的通信请求就准备齐全了。
接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的HTTP请求。
IP(Internet Protocol)网际协议位于网络层。几乎所有使用网络的系统都会用到IP协议。TCP/IP协议族中的IP指的就是网际协议。
IP协议的作用是把各种数据包传送给对方。而要保证确实传送到对方那里,则需要满足各类条件。其中两个重要的条件是IP地址和MAC地址(Media Access Control Address)。
IP地址指明了节点被分配到的地址,MAC地址是指网卡所属的固定地址。IP地址可以和MAC地址进行配对。IP地址可变换,但MAC地址基本上不会更改。
IP间的通信以来MAC地址。在网络上,通信的双方在同一局域网(LAN)内的情况是很少的,通常是经过多态计算机和网络设备中转才能连接到对方。而在进行中转时,会利用下一站中转设备的MAC地址来搜索下一个中转目标。这时,会采用ARP协议(Address REsolution Protocol)。ARP是一种用以解析地址的协议,根据通信方的IP地址就可以反查处对应的MAC地址。
在到达通信目标前的中转过程中,那些计算机和路由器等网络设备只能获悉很粗略的传输路线。这种机制成为路由选择(routing)。
TCP位于传输层,提供可靠的字节流服务。
所谓的字节流服务是指,为了传输方便,将大块数据分割成以报文段为单位的数据包进行管理。可靠的传输服务是指,能够把数据准确可靠地传给对方。
为了准确无误地将数据送达目标处,TCP协议采用了三次握手策略。
握手过程中使用了TCP的标志(flag)——SYN(synchronize)和ACK(acknowledgement)。
发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。
TCP的建立和关闭流程简单理解:
TCP的建立:三次握手(会占用一个端口)
客户端->服务器:我要给你发消息
服务器->客户端:你可以给我发消息了
客户端->服务器:好的
TCP的关闭:四次分手(释放端口资源)
客户端->服务器:我没有消息发给你了
服务器->客户端:好的
服务器->客户端:我也没有消息发给你了
客户端->服务器:好的
DNS(Domain Name System)服务是和HTTP协议一样位于应用层的协议。它提供域名到IP地址之间的解析服务。
DNS协议提供通过域名查找IP地址,或逆向从IP地址反查域名的服务。
URI(Uniform Resource Identifier),URL(Uniform Resource Locator,统一资源定位符),URL正是使用Web浏览器等访问Web页面时需要输入的网页地址。
URI用字符串标识某一互联网资源,而URL表示资源的地点(互联网上所处的位置)。可见URL是URI的子集。
URI格式:
URI格式构成简单理解就是由三部分组成:协议类型、服务器地址(和端口号)、路径
协议类型://服务器地址[:端⼝号]/路径
http://www.example.com/users?gender=male
其中,www.example.com 这个域名经过DNS后可以获取到ip地址,例如获取到的ip地址为222.222.222.222,ip地址就是提供给IP协议即网络层寻址路由使用的;222.222.222.222:80,80端口则是提供给传输层TCP使用的。
我们常说TCP的连接是有状态的,HTTP没有状态,这里的状态指的是,我发消息过去,我在传输层TCP不需要先说我是谁谁谁,然后我要发数据过去,而是直接就发数据,因为认识。
为什么会认识?通过什么认识?其实就是端口,TCP长连接是通过端口识别的,这里的端口就是对应java的Socket。Socket翻译为套接字其实比较难理解,实际Socket的翻译是端口/插口,TCP通过Socket端口读写流数据达到通信的目的。
肯定是先从客户端开始建立通信的,服务器在没有接收到请求之前不会发送响应。
无论是请求报文还是响应报文,基本都由三部分组成:请求行【请求报文】(状态行【响应报文】)、Headers、Body
HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能,于是引入了Cookie技术。
在说明HTTP提供的方法前,先说一下 REST
这个概念。
在网上有很多关于REST的介绍,事实上REST没有统一的答案,REST其实就是正确的使用HTTP的一种规范,即:
使用资源的格式来定义URL
规范地使用method来定义网络请求操作
规范地使用status code来表示响应状态
其他符合HTTP规范的设计准则
GET方法用来请求访问已被URI识别的资源。指定的资源经服务器端解析后返回响应内容。也就是说,如果请求的资源是文本,那就保持原样返回;如果是像CGI(Common Getway Interface,通用网关接口)那样的程序,则返回经过执行后的输出结果。
GET方法简单理解:
用于获取资源
对服务器数据不进行修改
不发送Body
虽然用GET方法也可以传输实体的主体,但一般不用GET方法进行传输,而是用POST方法。虽说POST的功能与GET很相似,但POST的主要目的并不是获取响应的主体内容。
POST方法简单理解:
用于增加或修改资源
发送给服务器的内容写在Body里面
鉴于HTTP/1.1的PUT方法自身不带验证机制,任何人都可以上传文件,存在安全性问题,一次一般的Web网站不使用该方法。若配合Web应用程序的验证机制,或架构设计采用REST(Representational State Transfer,表征状态转移)标准的同类Web网站,就可能会开放使用PUT方法。
PUT方法简单理解:
用于修改资源
发送给服务器的内容写在Body里面
HEAD方法和GET方法一样,只是不返回报文主体部分。用于确认URI的有效性及资源更新的日期时间等。
HEAD方法简单理解:
和GET使用方法完全相同
和GET唯一的区别在于,返回的响应中没有Body
DELETE方法用来删除文件,是与PUT相反的方法。DELETE方法按请求URI删除指定的资源。
HTTP/1.1的DELETE方法本身和PUT方法一样不带验证机制,所以一般的Web网站也不使用DELETE方法。当配合Web应用程序的验证机制,或遵守REST标准时还是有可能会开放使用的。
DELETE方法简单理解:
用于删除资源
不发送Body
上述的每次HTTP通信都需要经过三次握手连接和四次挥手断开,频繁通信是非常耗费资源的。
为解决上述TCP连接的问题,HTTP/1.1和一部分的HTTP/1.0想出了持久连接(HTTP Persistent Connections,也称为HTTP keep-alive或HTTp connection reuse)的方法。持久连接的特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态。
在HTTP/1.1中,所有的连接默认都是持久连接,但在HTTP/1.0内并未标准化。
持久连接使得多数请求以管线化方式发送成为可能。从前发送请求后需等待并收到响应,才能发送下一个请求。管线化技术出现后,不用等待响应亦可直接发送下一个请求。
保留无状态协议这个特征的同时又要解决类似的矛盾问题,于是引入了Cookie技术。Cookie技术通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
Cookie会根据从服务器端发送的响应报文内的一个叫做Set-Cookie的首部字段信息,通知客户端保存Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送出去。
服务器端发现客户端发送过来的Cookie后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
用于HTTP协议交互的信息被称为HTTP报文。请求端(客户端)的HTTP报文叫做请求报文,响应端(服务器端)的叫做响应报文。HTTP报文本身是由多行(用CR+LF作换行符)数据构成的字符串文本。
HTTP报文大致可分为报文首部和报文主体两块。两者由最初出现的空行(CR+LF)来划分。通常,并不一定要有报文主体。
发送邮件时,我们可以在邮件里写入文字并添加多份附件。这是因为采用了MIME(Multipurpose Internet Mail Extensions,多用途因特网邮件扩展)机制,它允许邮件处理文本、图片、视频等多个不同类型的数据。
HTTP协议中也采纳了多部分对象集合,发送的一份报文主体内可含有多类型实体。通常是在图片或文本文件等上传时使用。
多部分对象集合包含的对象如下:
multipart/form-data
在Web表单文件上传时使用
multipart/byteranges
状态码206(Partial Content,部分内容)响应报文包含了多个范围的内容使用。
在HTTP报文中使用多部分对象集合时,需要在首部字段里加上Content-type。
如果下载过程中遇到网络中断的情况,那就必须重头开始。为了解决上述问题,需要一种可恢复的机制。所谓恢复是指能从之前下载中断处恢复下载。
要实现该功能需要指定下载的实体范围。像这样,指定范围发送的请求叫做范围请求。
首部字段Range来指定资源的byte范围。
响应会返回状态码为206 Partial Content的响应报文。另外,对于多重范围的范围请求,响应会在首部字段Content-type表明multipart/byteranges后返回响应报文;如果服务器端无法响应范围请求,则会返回状态码200 OK和完整的实体内容。
状态码简单理解:
类别 | 原因 |
---|---|
1XX | 临时性消息。如:100 (继续发送)、101(正在切换协议) |
2XX | 成功。最典型的是 200(OK)、201(创建成功) |
3XX | 重定向。如 301(永久移动)、302(暂时移动)、304(内容未改变) |
4XX | 客户端错误。如 400(客户端请求错误)、401(认证失败)、403(被禁⽌)、404(找不到内容) |
5XX | 服务器错误。如 500(服务器内部错误) |
HTTP/1.1规范允许一台HTTP服务器搭建多个Web站点。
即使物理层只有一台服务器,但只要使用虚拟主机的功能,则可以假想已具有多台服务器。
如果一台服务器内托管了www.tricorder.jp和www.hackrjp这两个域名,当受到请求时就需要弄清楚究竟要访问哪个域名。
在相同的IP地址下,由于虚拟主机可以寄存多个不同主机名和域名的Web网站,因此在发送HTTP请求时,必须在Host首部内完整指定主机名或域名的URI。
代理、网关和隧道可以将请求转发给通信线路上的下一站服务器,并且能接收从那台服务器发送的响应再转发给客户端。
代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
使用代理服务器的理由有:利用缓存技术减少网络带宽的流量,组织内部针对特定网站的访问控制,以获取访问日志为主要目的,等等。
代理按两种基准分类:一种是是否使用缓存,另一种是是否会修改报文。
代理转发响应时,缓存代理会预先将资源的副本(缓存)保存在代理服务器上。
当代理再次接收到相同资源的请求时,就可以不从源服务器那里获取资源,而是将之前缓存的资源作为响应返回。
转发请求或响应时,不对报文做任何加工的代理类型被称为透明代理。反之,对报文内容进行加工的代理被称为非透明代理。
网关是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的服务器一样对请求进行处理。
利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。
隧道是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序。
隧道可按要求建立起一条与其他服务器的通信线路,届时使用SSL等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。
隧道本身不会去解析HTTP请求。
缓存服务器是代理服务器的一种,并归类在缓存代理类型中。换句话说,当代理转发从服务器返回的响应时,代理服务器将会保存一份资源的副本。
缓存服务器的优势在于利用缓存可避免多次从源服务器转发资源。
即使存在缓存,也会因为客户端的要求、缓存的有效期等因素,向源服务器确认资源的有效性。若判断缓存失效,缓存服务器将会再次从源服务器上获取新资源。
浏览器缓存如果有效,就不必再向服务器请求相同的资源了,可以直接从本地磁盘内读取。
另外,和缓存服务器相同的一点是,当判定缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新资源。
Header
首部的作用是HTTP消息的metadata。
Host
指的是目标主机。这里需要注意,Host并不是在网络上用于寻址的,而是在目标服务器上用于定位子服务器的(比如一个网站 www.baidu.com
,这个服务器下是有多个子服务器的)。
Content-Type
指定Body的类型。主要有四类:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 853
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
....
POST /users HTTP/1.1
Host: api.github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=example&gender=male
Retrofit的代码:
@FormUrlEncoded
@POST("/users")
Call<User> addUser(@Field("name") String name, @Field("gender") String gender);
POST /users/ HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=---
WebkitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 2382
------WebkitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"
vincent
------WebkitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="avatar";
filename="avatar.jpg"
Content-Type: image/jpeg
JFIFHHv0w9jximQrWa......
------WebkitFormBoundary7MA4YWxkTrZu0gW
Retrofit的代码:
@Multipart
@POST("/users")
Call<User> addUser(@Part("name") RequestBody name, @Part("avatar") RequestBody avatar);
...
RequestBody namePart = RequestBody.create(MediaType.parse("text/plain"), nameStr);
RequestBody avatarPart = RequestBody.create(MediaType.parse("image/jpeg"), avatarFile);
api.addUser(namePart, avatarPart);
请求提交json
POST /users HTTP/1.1
Host: example.com
Content-Type: application/json; charset=utf-8
Content-Length: 38
{"name:"vincent", "gender":"male"}
Retrofit代码:
@POST("/users")
Call<User> addUser(@Body("user") User user);
响应返回json
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 234
[{"login":"vincent", "id":1, "node_id":"xxxxx", "avatar_url":"https://avaars.githubusercontent.com/u/1?v=4", .....}]
-----------------------------------------------------------------------
请求提交二进制内容
POST /user/1/avatar HTTP/1.1
Host: example.com
Content-Type: image/jpeg
Content-Length: 1575
JFIFHH9.......
Retrofit代码:
@POST("user/{id}/avatar")
Call<User> updateAvatar(@Path("id") String id, @Body RequestBody avatar);
RequestBody avatarBody = RequestBody.create(MediaType.parse("image/jpeg", avatarFile);
api.updateAvatar(id, avatarBody);
响应返回二进制内容
HTTP/1.1 200 OK
content-type: image/jpeg
content-length: 1575
JFIFHH9....
Content-Length
指定Body的长度(字节)。
用于当响应发起时,内容长度还没能确定的情况下。和 Content-Length
不同时使用。用于是尽早给出响应,减少用户等待。
格式如下:
HTTP/1.1 200 OK
Content-Type: text/html
Transfer-Encoding: chunked
4
Chun
9
ked Trans
12
fer Encoding
0
Location
指定重定向的目标URL。
用户代理,即是谁实际发送请求、接受响应的,例如手机浏览器、某款手机App。
Range/Accept-Range
能按范围请求数据。
Accept-Range: bytes
响应报文中出现,表示服务器支持按字节来取范围数据
Range: bytes=
请求报文中出现,表示要取哪段数据。start和end指定取得字节范围
Content-Range:
响应报文中出现,表示发送的是哪段数据
作用:常用于断点继传、多线程下载
Accept:客户端能接受的数据类型。如 text/html
Accept-Charset:客户端接受的字符集。如 utf-8
Accept-Encoding:客户端接受的压缩编码类型。如 gzip
Content-Encoding:压缩类型。如 gzip
HTTP主要有这些不足,举例如下:
通信使用明文(不加密),内容可能会被窃听
不验证通信方的身份,因此有可能遭遇伪装
无法证明报文的完整性,所以有可能已遭篡改
TCP/IP是可能被窃听的网络,按TCP/IP协议族的工作机制,通信内容在所有的通信线路上都有可能遭到窥视。
窃听相同段上的通信并非难事。只需要收集在互联网上流动的数据包(帧)就行了。对于收集来的数据包解析工作,可交给那些抓包或嗅探器工具。(所谓的抓包就是抓取没有通过SSL加密的在IP协议传输的报文段,抓到报文数据端后再解析出来)
用SSL建立安全通信线路之后,就可以在这条线路上进行HTTP通信了。与SSL组合使用的HTTP被称为HTTPS。
由于HTTP协议中没有加密机制,那么就对HTTP协议传输的内容本身加密。即把HTTP报文里所含的内容进行加密处理。由于该方式不同于SSL或TLS将整个通信线路加密处理,所以内容仍有被篡改的风险。
添加了加密及认证机制的HTTP称为HTTPS。
HTTPS(HTTP Secure/HTTP over SSL/HTTP over TLS)并非是应用层的一种新协议。只是HTTP通信接口部分用SSL(Secure Socket Layer)和TLS(Transport Layer Security)协议代替而已,SSL是TLS的前身。
客户端在应用层(HTTP)将数据传给传输层(TCP)前,会将数据经过安全层进行加密
服务器在传输层(TCP)将数据给应用层(HTTP)前,也会将数据经过安全层进行解密
HTTPS的本质原理是,在客户端和服务器之间用非对称加密协商出一套对称密钥,每次发送信息之前将内容加密,收到之后解密,达到内容的加密传输
在详细介绍怎么使用HTTPS通信之前,有必要先了解在HTTPS中使用的加密技术:对称加密和非对称加密。
对称加密
就是通信双方完全使用同一个密钥,使用加密算法配合上密钥来加密,解密时使用加密过程的完全逆过程配合密钥来进行解密(即通信双方密钥相同,加密和解密算法不同)。
经典的算法:
DES(56位密钥,密钥太短而逐渐被弃用)
AES(128位、192位、256位密钥,现在最流行)
对称加密的作用:加密通信,防止信息在不安全网络上被截获后,信息被人读取或篡改
对称加密(如AES)的破解思路:
拿到一组或多组原文-密文对
设法找到一个密钥,这个密钥可以将这些原文-密文中的原文加密为密文,以及将密文解密为原文的组合,即为成功破解
反破解:一种优秀的对称加密算法的标准是,让破解者找不到比穷举法(暴力破解法)更有效的破解手段,并且穷举法的破解时间足够长(例如数千年)
对称加密的缺点:密钥泄露,不能在不安全网络上传输密钥,一旦密钥泄露则加密通信失败
非对称加密
就是发送端使用接收端提供的公钥对数据进行加密得到密文;服务器使用私钥将客户端使用公钥加密的密文解密得到原数据(即发送端持有公钥加密,接收端持有私钥解密,加解密算法相同,密钥不同)。
使用非对称加密通信,可以在不可信网络上将双方的公钥传给对方,然后再发消息前分别对消息使用对方的公钥来加密和使用自己的私钥来签名,做到不可信网络上的可靠密钥传播及加密通信。
由于私钥和公钥互相可解,因此非对称加密还可以应用于数字签名技术。
通常会对原数据hash以后对hash签名,然后附加在原数据后面作为签名。这是为了让数据更小。
经典算法:
RSA(可用于加密和签名)
DSA(仅用于签名,但速度更快)
非对称加密的优缺点:
优点:可以在不安全网络上传输密钥
缺点:计算复杂,因此性能相比对称加密差很多
非对称加密的破解:
和对称加密不同之处在于,非对称加密的公钥很容易获得,因此制造原文-密文对是没有困难的事
所以,非对称加密的关键在于,如何找到一个正确的私钥,可以解密所有经过公钥加密过的密文。找到这样的私钥即为成功破解
由于非对称加密的自身特性,怎样通过公钥推断出私钥通常是一种思路,但往往最佳手段依然是穷举法,只是和对称加密破解的区别在于,对称加密破解是不断尝试自己的新密钥是否可以将自己拿到的原文-密文对进行加密和解密,而非对称加密时不断尝试自己的新私钥是否和公钥互相可解
反破解:和对称加密一样,非对称加密算法优秀的标准同样在于,让破解者找不到比穷举法更有效的破解手段,并且穷举法的破解时间足够长
SSL采用一种叫做公开密钥加密的加密处理方式。
以对称加密的方式加密时必须将密钥也发送给对方。可究竟怎样才能安全地转交?在互联网上转发密钥时,如果通信被监听那么密钥就可能会落入攻击者之手,同时也就失去了加密的意义。另外还得设法安全地保管接收到的密钥。
非对称加密的方式很好的解决了对称加密的困难。
公开密钥加密使用一对非对称的密钥。一把叫做私有密钥,另一把叫做公开密钥。顾名思义,私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得。
使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不用担心密钥被攻击者窃听而盗走。
HTTPS采用对称加密和非对称加密两者并用的混合加密机制。
在交换密钥环节使用公开密钥加密方式,之后的简历通信交换报文阶段则使用共享密钥加密方式。
数字证书认证机构的业务流程:首先,服务器的运营人员向数字证书认证机构提出公开密钥的申请。数字证书认证机构在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公钥证书后绑定在一起。
服务器会将这份由数字证书认证机构颁发的公钥证书发送给客户端,以进行公开密钥加密方式通信。公钥证书也可叫做数字证书或直接称为证书。
接到证书的客户端可使用数字证书认证机构的公开密钥,对那张证书上的数字签名进行验证,一旦验证通过,客户端便可明确两件事:一,认证服务器的公开密钥的是真实有效的数字证书认证机构。二,服务器的公开密钥是值得信赖的。
此处认证机关的公开密钥必须安全地转交给客户端。使用通信方式时,如何安全转交是一件很困难的事,因此,多数浏览器开发商发布版本时,会事先在内部植入常用认证机关的公开密钥。
证书的一个作用是用来证明作为通信一方的服务器是否规范,另外一个作用是可确认对方服务器背后运营的企业是否真实存在。拥有该特性的证书就是EV SSL证书。
持有EV SSL证书的Web网站的浏览器地址栏处的背景是绿色的。
HTTPS中还可以使用客户端证书。以客户端证书进行客户端认证,证明服务器正在通信的对方始终是预料之内的客户端,其作用跟服务器证书如出一辙。
现状是,安全性极高的认证机构可颁发客户端证书但仅用于特殊用途的业务。例如,银行的网上银行就采用了客户端证书。在登陆网银时不仅要求用户确认输入ID和密码,还会要求用户的客户端证书,以确认用户是否从特定的终端访问网银。
HTTPS连接的大致流程:
客户端请求建立TLS连接
服务器发回证书
客户端验证服务器证书
客户端信任服务器后,和服务器协商对称密钥
使用对称密钥开始通信
流程再简化可以理解为:
客户端与服务器协商非对称密钥
客户端与服务器协商对称密钥
接下来说明HTTPS建立连接的具体步骤:
1、客户端发送Client Hello
客户端发送数据给服务器协商挑选使用的TLS版本信息等,如:可选的TLS版本集合、可选的Cipher Suite加密套件集合、客户端随机数
2、服务器发送Server Hello
服务器发送数据给客户端确定使用的TLS版本信息等,如:选择的TLS版本、选择的Cipher Suite加密套件、服务器随机数
注:客户端随机数和服务器随机数在上面的两个步骤虽然都是明文传输的,但这两个随机数的加入能降低被破解的风险,在后续的步骤会使用到。这涉及到数学和社会工程学等处理,不再赘述。
3、服务器发回证书
这里的证书指的是用于给客户端验证的服务器公钥等信息。具体有:
服务器公钥、服务器的名字、服务器的地区、服务器的公钥的签名、服务器的主机名
证书签发机构的公钥、证书签发机构的名字、证书签发机构的地区、证书签发机构的公钥的签名
根证书机构的公钥、根证书机构的地址、根证书机构的地区
知道上面需要的证书信息是其次,更重要的是理解为什么需要这些数据,接下来具体分析。
服务器公钥用于客户端在发送数据时加密使用,服务器接收到数据后就可以使用私钥解密。
因为网络是不安全的,所以 客户端在使用服务器公钥前需要先验证它的合法性,所以还需要服务器发来服务器公钥的签名。
服务器公钥的签名即服务器公钥的hash值,签名发送给客户端时是使用私钥加密过的,在客户端会使用公钥解密,并用该hash值与服务器公钥hash后的值对比是否一致即可验证服务器公钥是否合法(非对称加密的指纹验证操作,服务器公钥只是一段数据,并不是用于解密服务器公钥的签名的公钥)。
所以我们 还需要用于解密服务器公钥的签名的公钥(因为服务器把服务器的公钥的签名发送给客户端时是用私钥加密的,需要私钥配对的公钥),或者说是 用于验证这个公钥签名的另一个公钥,它有一个更准确的名字是 证书签发机构的公钥。
只要证书签发机构的公钥是安全的,最终验证服务器公钥合法性时才是安全的。为了验证证书签发机构的公钥的合法性,就需要 证书签发机构的公钥的签名 还有 证书签发机构的公钥的签名的公钥。
或许你会想到,因为又无法相信证书签发机构的公钥的签名的公钥,所以又需要验证这个公钥的签名和解密的公钥,这样无限循环下去的情况。
但这个问题是不用担心的,现实情况是,当我们验证到证书签发机构的公钥的签名的公钥,即证书签发机构的签发机构的公钥时,也就是我们常说的 根证书,手机或电脑的根证书,会提前被植入到操作系统中,操作系统认为可信作为最终保证验证的安全性。
根证书是否也存在不可信?是有存在的情况,但一般不会,需要我们无条件的认为根证书可信。
在浏览器访问网站的时候,就可以看到对应的证书:
但实际上,浏览器上 每一个证书都是一个数据,所以证书里面不仅包含公钥,还有一些相关的信息,例如名字、地区等。
但有了以上的证书,还不能保证安全性,还存在一种场景:
有两个网站,一个是 www.baidu.com,一个是 www.bad.com,这两个网站都有合法的证书,www.bad.com 去监视 www.baidu.com;当客户端访问 www.baidu.com 时,www.bad.com 的网站拦截了这个操作,并把自己的合法证书发给了客户端,因为证书也是合法的,所以能处理后续的操作,实际上已经攻击了。
所以 客户端除了验证服务器公钥的合法性外,还需要验证服务器的主机名(一般是网站的域名比如 www.baidu.com ,验证服务器给的证书是签发给 www.baidu.com)。
或许你会想到,为什么 www.bad.com 如果把域名改为 www.baidu.com 是不是又可以了?其实是不行的,服务器发回证书时,同时会发来服务器的公钥的签名,这个签名是服务器的公钥、服务器的名字、服务器的地区、服务器的主机名这几个数据的签名,所以即使修改了主机名,最终也会验证服务器公钥不合法无法建立HTTPS连接。
小结:
服务器发回证书的操作,最终的结果是拿到用于后续与服务器通信加密使用的服务器公钥并验证它的合法性,以及访问正确的服务器主机名。
服务器公钥的签名是为了验证服务器公钥的合法性
证书签发机构的公钥是为了非对称加密通信时解密服务器公钥的签名
证书签发机构的公钥的签名是为了验证证书签发机构的公钥的合法性
根证书是为了非对称加密通信时解密证书签发机构的公钥的签名
服务器主机名是为了验证访问的是正确的服务器
4、客户端发送 Pre-master secret
客户端和服务器各自用客户端随机数、服务器随机数和Pre-master secret计算出Master secret,Master secret再计算出四个东西:
客户端加密密钥
服务器加密密钥
客户端MAC secret
服务器MAC Secret
在步骤3已经完成了非对称加密通信的验证,所以这一步是非对称加密通信传输给服务器。
到这一步客户端和服务器对称密钥已经协商完毕。
后续交互时:
客户端用客户端加密密钥加密后发送数据,服务器也用客户端加密密钥解密
服务器用服务器加密密钥加密后发送数据,客户端也用服务器加密密钥解密
(1)客户端加密密钥和服务器加密密钥
为什么要分成两个密钥(客户端加密密钥和服务器加密密钥),用一个不行吗?分成两个主要是为了防止数据被看不懂的攻击者直接扔回来。
举个不大严谨但能解释的例子:
客户端用加密密钥发送了一个数据说:分手吧。此时有一个攻击者拦截了数据,但看不懂你的数据,然后就直接把你的数据仍回去给客户端,这样就出现了数据错误。
所以客户端加密密钥加密的数据就用客户端加密密钥解密,服务器加密密钥加密的数据就用服务器加密密钥解密。
(2)客户端MAC secret和服务器MAC secret
MAC secret其实是HMAC secret,即hash-based message authenticate code,基于hash的消息认证码,也可以翻译为带有密钥的hash算法。
客户端或服务器在发送数据时不仅做了加密,还会做一次HMAC,HMAC的作用不仅能验证数据的完整性,还能验证数据是由谁发送过来的,别人因为没有密钥所以不能制造出这样的MAC,就类似于非对称加密时的指纹,但和非对称加密指纹不同的是它不像非对称加密的指纹那样拿到公钥谁都可以验证,HMAC只有拥有私钥密钥的人才可以验证。
5、客户端通知:将使用加密通信
6、客户端发送:Finished
7、服务器通知:将使用加密通信
8、服务器发送:Finished