为了验证HTTP的隧道代理。我们用抓包分析验证过程
当客户端向Proxy发起Http CONNECT Method的时候,就是告诉Proxy,先在Proxy和目标服务器之间先建立起连接,在这个连接建立起来之后,目标服务器会返回一个回复给Proxy,Proxy将这个回复转发给客户端,这个Response是Proxy跟目标服务器连接建立的状态回复,而不是请求数据的Response。在此之后,客户端跟目标服务器的所有通信都将使用之前建立起来的建立。这种情况下的Http隧道,Proxy仅仅实现转发,而不会关心转发的数据。这也是为什么在使用Proxy的时候,Https请求必须首先使用Http CONNECT建立隧道。因为Https的数据都是经过加密的,Proxy是无法对Https的数据进行解密的,所以只能使用CONNECT,仅仅对通信数据进行转发。
本图来自《HTTP权威指南》
HTTP 客户端通过HTTP的CONNECT方法请求隧道代理,创建一条到达任意目的服务器和端口的TCP连接,并对客户端和服务器之间的后继数据进行盲转发。
可以看到,浏览器与代理进行 TCP 握手之后,发起了 CONNECT 请求,报文起始行如下:
对于 CONNECT 请求来说,只是用来让代理创建 TCP 连接,所以只需要提供服务器域名及端口即可,并不需要具体的资源路径。代理收到这样的请求后,需要与服务端建立 TCP 连接,并响应给浏览器这样一个 HTTP 报文:
3.2 HTTPS会话过程
1)客户端发送client hello开始ssl通信,其中包含报文包含客户端支持的一套加密规则(SSL指定版本,加密组件cipher suite)发送给网站。
其中部分关键信息:
ProtocolVersion client_version; //客户端支持的TLS最高版本
Random random; //客户端生成的随机数,用于之后生成会话密钥
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>; //支持的加密算法,如RSA
CompressionMethod compression_methods<1..2^8-1>; //支持的压缩算法
2服务器可进行SSL通信时,会响应报文server hello ,报文中包含SSL版本及加密组件(加密组件是从接收到客户端加密组件内筛选出来的)。之后服务端将自己的身份信息以证书的形式发回给客户端。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
部分字段说明:
ProtocolVersion server_version; //确定使用的SSL/TLS版本
Random random; //服务器端生成的随机数,之后用于生成会话密钥
SessionID session_id;
CipherSuite cipher_suite; //确定使用的加密算法
CompressionMethod compression_method; //确实使用的压缩算法
其中选择的加密算法:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
3证书
可以看出百度用的是verisign的证书
4.1 Server Key Exchange Message(可选)
这条消息是用于会话密钥(premaster secret)生成的,首先这条消息不是必须的,只有在客户端与服务器端双方协商采用的加密算法是DHE_DSS、DHE_RSA、DH_anon时Server Certificate消息中的服务器证书中的信息不足以生成premaster secret时服务器端才需要发送此消息。
4.2 最后服务端发送server hello done通知客户端,表示服务器端已经完成了为支持密钥交换(生成premaster key)所做的事情,下面开始等待客户端为密钥交换所要发的消息,最初阶段的SSL握手协商阶段结束。
SSL第一次握手结束
*****************************************************************************
如果此次HTTPS连接是双向认证的话,服务器就可以通过此消息请求客户端证书以便进行身份验证。
我们是从百度抓取数据,只需要服务端认证,所以不会发送此消息,这里没有截图。
客户端在收到Server Hello Done与Certificate Request后,表示服务器端需要验证客户端证书,这时客户端需要发送此Client Certificate消息给服务器端,即使没有客户端证书也要发个 certificate_list 列表为空的此消息,服务器端未收到或收到此消息后验证客户端证书,验证结果可酌情处理,如关闭加密通信或忽略此错误等。
********************************************************************************
5.获得服务端证书之后客户端要做以下工作:
客户端收到Server Hello Done消息后就发送此消息用于将生成的premaster secret(用于生成会话密钥master secret)发送到服务器端。根据所采用的非对称加密的算法不同生成premaster secret的方式有所区别。
- RSA :premaster_secret*是由客户端生成的随机数,用服务器证书中公钥加密后传输,服务器端采用私钥解密。
- Diffie-Hellman: 对于Diffie-Hellman算法而言,双方通过Server Key Exchange Message 与 Client Key Exchange Message 交换两个数即可各自算出相同的加密密钥,用此当premaster_secret。
服务器收到此消息后终于可以生成会话密钥了,我们要解答几个问题:
为何要生成master secret?
RSA等非对称加密算法加密大量数据时的性能远小于AES等对称加密算法,所以前面所做的 Key Exchange 操作都是为了生成一个对称加密算法的密钥,以便提高通信性能。
master secret的生成方法?
使用前面协商的如SHA_256哈希算法由三个随机数client_random、server_random、premaster_secret生成一个安全随机数,采用三个随机数是为了随机性更强。
三个随机数都在不可靠网络上传输,如何保证master secret是保密的?
三个随机数中premaster_secret是保密的,所以master_secret也是保密的。
c) 客户端继续发送change cipher Spec报文(Client发送密钥改变通知)
用来告诉服务端,此报文之后的通信会采用master secret进行加密。
d)客户端发送finish报文(Encrypted Handshake Message),
报文包含链接至今全部报文的整体校验值。客户端将前面的握手消息生成摘要再用协商好的秘钥加密,这是客户端发出的第一条加密消息。服务端接收后会用秘钥解密,能解出来说明前面协商出来的秘钥是一致的。
6.服务端接收客户端发来的数据之后要做以下的操作:
a) 使用自己的私钥将信息解密取出密码,使用密码解密客户端发来的握手消息,并验证HASH是否与客户端发来的一致。
b) Certificate Verify:如果服务器请求验证客户端,则这消息允许服务器完成验证过程。
7 服务端发送Change cipher spec报文:
服务器端收到 Client Key Exchange Message 通知后即可通知客户端已生成master secret,后面的通信数据用此key加密。
8 服务端发送finish报文(Encrypted Handshake Message)。
服务端也会将握手过程的消息生成摘要再用秘钥加密,这是服务端发出的第一条加密消息。客户端接收后会用秘钥解密,能解出来说明协商的秘钥是一致的。
到这里,双方已安全地协商出了同一份秘钥,所有的应用层数据都会用这个秘钥加密后再通过 TCP 进行可靠传输。
本文初始目的是为了整理HTTP代理过程,顺带回顾下HTTPS的流程。
举的HTTPS例子也是简单的。单项抓取百度的数据,用抓包工具验证过程。
还有双向验证的,以及高级的优化功能。很底层的加密算法没有去深入展开。
这部分知识在平时的开发过程中很少用到,因为常用都是高级封装好的接口(比如HTTPclient等),但能让我们更清楚地了解 HTTPS 的工作原理,而不仅仅是只知道 HTTPS 会加密数据十分安全。
感谢唐斌斌对于抓包过程的支持。
参考:
https://imququ.com/post/web-proxy.html
http://www.jianshu.com/p/7158568e4867
http://blog.csdn.net/tterminator/article/details/50675540
http://blog.csdn.net/tp7309/article/details/53057429