对于客户端和服务器来讲,应用层协议还是HTTP,只是加了一个SSL层来做加密解密的逻辑,对应用层来说是透明的,在网络上传输的都是加密的请求和加密的响应,从而达到安全传输的目的
这是SSL层在网络模型的位置,SSL属于应用层协议。接管应用层的数据加解密,并通过网络层发送给对方。
更细地分,SSL协议分握手协议和记录协议,握手协议用来协商会话参数(比如会话密钥、应用层协议等等),记录协议主要用来传输应用层数据和握手协议消息数据,以及做加解密处理。我们应用层的的消息数据在SSL记录协议会给分成很多段,然后再对这个片段进行加密,最后在加上记录头后就发送出去。
TLS握手协议
SSL/TLS 握手协议又细分为四个子协议,分别是握手协议、密码规格变更协议、警告协议和应用数据协议。
、
完整的握手流程
首先是TCP握手,TCP三次完成之后才进入SSL握手,SSL握手总是以ClientHello消息开始,就跟TCP握手总是以SYN包开始一样;
ClientHello主要包含客户端支持的协议、密钥套件、session id、客户端随机数、sni、应用层协议列表(http/1.1、h2)、签名算法等等;
服务器收到ClientHello之后会从中选取本次通信的协议版本、密钥套件、应用层协议、签名算法,以及服务器随机数,然后通过ServerHello消息响应,如果没有session复用,那将服务器的证书通过Certificate消息响应,如果是选用了ECC算法来做密钥交换的话,那还会将椭圆曲线参数以及签名值通过ServerKeyExchange消息发送给对方,最后通过ServerHelloDone消息来结束本次协商过程;
客户端收到这些消息之后,会生成预主密钥、主密钥和会话密钥,然后将椭圆曲线参数通过ClientKeyExchange发送给服务器,服务器拿到客户端的椭圆曲线参数也会生成预主密钥,如果是RSA的话ClientKeyExchange用来发送公钥加密的预主密钥,服务器用私钥来解密一下就可以得到预主密钥,再由预主密钥生成主密钥和会话密钥。最后双方都以ChangeCipherSpce和Finished消息来告知对方,自己已经准备好了可以进行加密通信了,然后握手完成。
可以看出,完整的SSL握手需要2个RTT,而且每次握手都用到了非对称加密算法签名或者解密的操作,比较耗CPU,所以后来就有了session复用的优化手段,后面会做介绍。
SSL的握手消息虽然比较多,但很多消息都是放在一个TCP包中发送的,从抓包可以看出完整的SSL握手需要2个RTT。
刚才通过完整的SSL握手可以看出几个缺点:
2个RTT,首包时间较长
每次都要做非对称加密,比较耗CPU,影响性能
每次都要传证书,证书一般都比较大,浪费带宽
SSL握手的目的是协商会话密钥以及参数,如果能把这些会话参数缓存那就可以没有必要每次都传证书和做非对称加密计算,这样就可以提高握手性能,而且可以将RTT减到1个,提高用户体验。
所以就有了session id的复用方式,每次完整握手之后都将协商好的会话参数缓存在服务器中,客户端下次握手时会将上次握手的session id带上,服务器通过session id查询是否有会话缓存,有的话就直接复用,没有的话就重新走完整握手流程。但是session id这种方式也有缺点,比较难支持分布式缓存以及耗费服务器的内存。
而session ticket 方案,其原理可以理解为跟 http cookie 一样,服务器将协商好的会话参数加密成 session ticket,然后发送给客户端,客户端下次握手时会将这个session ticket带上,服务器解密成功就复用上次的会话参数,否则就重新走完整握手流程。可以看出session ticket这种方式不需要服务器缓存什么,支持分布式环境,有很大的优势。这种方式有一个缺点是并不是所有客户端都支持,支持率比较低,但随着客户端版本的更新迭代,以后各种客户端会都支持。因为session id客户端支持率比较高,所以目前这两种方式都在使用。
这是 session ticket 的复用,跟 session id 的区别是 client hello 会带上 Session ticket ,将近200个字节的加密数据,服务器会用session ticket 密钥来解密,解密成功则直接复用,也简化了握手流程,提升效率和性能。
这是我们上了分布式 Session id 复用的效果,session id复用的比例在没有开启分布式缓存时只占了3%左右,复用率很低,上了分布式session缓存之后,这个比例提升到20%多。握手时间也从75ms左右降低到65ms左右,性能提升效果还是很明显的。
SSL/TLS握手时的私钥用途(RSA、ECDHE)
我们知道私钥是整个SSL中最重要的东西,那私钥在SSL握手里面又是怎么使用的呢?两种使用方式分别是:使用RSA来做密钥交换和使用ECDHE来做密钥交换。对于RSA来说,客户端生成预主密钥,然后用公钥加密再发给服务器,服务器用私钥来解密得到预主密钥,然后由预主密钥生成主密钥,再由主密钥生会话密钥,最后用会话密钥来通信。
对于ECDHE来说,客户端和服务器双方是交换椭圆曲线参数,私钥只是用来签名,这是为了保证这个消息是持有私钥的人给我发的,而不是冒充的。双方交换完参数之后生成预主密钥,再生成主密钥和会话密钥。这就跟刚才RSA后面的流程一样了。
可以看出RSA和椭圆曲线密钥交换算法的私钥用途是不一样的,RSA密钥交换时是用来做加解密的,椭圆曲线密钥交换时是用来做签名的。
SSL/TLS中的密钥
预主密钥、主密钥和会话密钥,这几个密钥都是有联系的。
对于RSA来说,预主密钥是客户端生成,加密之后发给服务器,服务器用私钥来解密。对于ECDHE来说,预主密钥是双方通过椭圆曲线算法来生成。
主密钥是由预主密钥、客户端随机数和服务器随机数通过PRF函数来生成;会话密钥是由主密钥、客户端随机数和服务器随机数通过PRF函数来生成,会话密钥里面包含对称加密密钥、消息认证和CBC模式的初始化向量,但对于非CBC模式的加密算法来说,就没有用到这个初始化向量。
session 缓存和session ticket里面保存的是主密钥,而不是会话密钥,这是为了保证每次会话都是独立的,这样才安全,即使一个主密钥泄漏了也不影响其他会话。
Keyless
刚才提到RSA和ECDHE的私钥用途,对于服务器来说,密钥交换算法是RSA时,私钥是用来做解密的,ECDHE时,私钥是用来签名的。
Keyless就是将私钥参与运算的部分分离远程服务器,这样可以解决两个问题:
公私钥分离,用户不需要将私钥给第三方,减少私钥泄露的风险。
将非对称加密运算这种cpu密集型运算剥离到keyserver,可以在keyserver中安装ssl加速卡做硬件加速,提高性能。
HTTP/2
HTTP/2主要有这几个特性:
二进制协议,可以做更多的优化;
多路复用,一定程度上解决了队头阻塞的问题,提高并发和总的加载时间
头部压缩,很多请求头或者响应头都没有必要重复传输浪费带宽,比如user-agent、cookie还
其他没有必要每次都传的头部在http2中都做了优化
安全,虽然RFC规定HTTP/2可以运行在明文之上,但目前所有浏览器都只支持https之上的http/2,所以是安全的。
开启HTTP/2会有这几个收益:
一、加载时间的提升,我们做了这么一个测试,一张大图片分割成几百个小图片,然后用http/1.1和http/2打开,http/1.1加载完所有小图片用了五六秒,但http/2加载完所有小图片只需要不到1s的时间,有5倍左右的提升,效果还是很明显的,具体提升百分之多少这得具体看具体的业务类型。
二、头部压缩有95%左右的提升,http/1.1的平均响应头大小有500个字节左右,而http/2的平均响应头大小只有20多个字节,提升非常大,所以http/2非常适合小图片小文件的业务类型,因为小文件的http头部比重较大,而http/2对头部的压缩做了非常好的优化。
三、服务器QPS的性能有60%多的提升,这是因为http/2的连接复用和多路复用机制,可以处理更多的并发请求。
登录 | 立即注册