从网络原理来看SSL安全协议

网络安全是当前非常受到关注的一个问题。大部分站点都是通过https来实现自己的数据安全的,那么怎么样才能把自己的站点编程https站点呢?我们需要了解ssl协议。

SSL的全称是Secure Sockets Layer,现在我们很多时候在使用TLS也就是Transport Layer Security很多同学可能不是很了解TLS是怎么样的一个协议,我们简单看一下他的发展过程。

SSL是由网景公司在1995年推出的,在3.0版本获得了一个非常大的发展。接下来微软把自己的IE浏览器捆绑windows一起卖出以后,导致网景遇到很大的困境,网景把SSL协议交给IETF组织。

在1999年,应微软的要求IETF把SSL更名为TLS1.0,那么,后面在06,08到2018年TLS分别发布了1.1,1.2和1.3协议。

那么TLS协议究竟是怎样保证http的明文消息被加密的呢?我们这里简述一下TLS的通用模型。

在ISO/OSI七层模型中,应用层是http协议,在应用层之下,我们的表示层也就是TLS所发挥作用的这一层,他通过握手,交换密钥,告警,对称加密的方式使http层没有感知的情况下做到了数据的安全加密,那么TLS究竟是怎样做到了数据的安全加密的呢。

我们可以看到TLS的安全密码套件,当我们抓包或者观察服务端配置时,我们可以看到安全密码的配置,这个安全密码的配置呢他决定了我们的TLS协议是怎样保证明文被加密的。这里大概有四个组成部分。

第一个组成部分叫做密钥交换,也就是ECDHE,这实际上是一个椭圆曲线加密算法的表达,密钥交换是为了解决浏览器和服务器之间怎样各自独立的生成密钥,而最后生成的密钥是相同的接下来他们会用这个密钥去加密数据。

那么在密钥交换这个过程中,我们需要让浏览器和服务器各自去验证对方的身份,而验证身份是需要一个算法的,这个算法叫做RSA,他用于身份验证。

接下来我们进行数据加密,解密这样的通讯的时候呢,我们需要用到对称加密算法而对称加密算法里AES_128——GCM就是表达这样一个对称加密算法,其中第一个部分AES,他表达了是怎样一种算法,第二个部分128表示了AES算法里支持了3种加密强度,我们使用了128位这样的一个加密强度。AES中有很多分组模式,其中GCM是一种比较新的分组模式,他可以提高多核CPU情况下加密和解密的一个性能。

SHA_256是一个摘要算法,他用来把不定长度的字符串生成固定长度的更短的一个摘要。

以上是TLS算法的概要介绍,下面我们来看一下对称加密算法和非对称加密算法有什么不同。

对称加密与非对称加密各自的应用场景

下面我们来看一下对称加密算法和非对称加密算法有什么不同。

在对称加密这样一个场景中,两个想通讯的人张三和李四,他们共同持有同一把密钥,张三可以把原始明文的文档,通过这一把密钥加密生成一个密文文档,而李四拿到这个文档以后呢,他可以用这把密钥还原转换为原始的明文文档,而中间的任何人如果没有持有这把密钥,即使他知道了对称加密的算法他也没有办法把密文还原成原始文档。

那么对称加密究竟是怎么实现的呢? 我们可以以RC4这样一个对称加密的序列算法来描述一下。

我们使用异或(xor)操作, 他是一个位操作,比如1和0进行异或得到1,0和1也得到了1,那么相同的1和1或者0和0进行异或操作都会得到0。

在这样一个场景下1010是我们共同持有的密钥,0110是我们的明文,张三执行加密的时候就会得到密文1100。

1 0 1 0 // 密钥
  xor
0 1 1 0 // 明文
  ||
1 1 0 0 // 密文

异或有一个对称的特性,就是把密文与密钥同样的做异或操作,比如1和1得到0, 1和0得到1,0和1得到1, 0和0得到0。

1 0 1 0 // 密钥
  xor
1 1 0 0 // 密文
  ||
0 1 1 0 // 明文

密文可以用同一把密钥完全还原成了明文,所以对称加密有一个最大的优点就是他的性能非常的好,他只要遍历一次就可以得到我们最终的密文,解密的过程也是一样,而非对称加密他的性能就会差很多。

我们看一下非对称加密算法,他根据一个数学原理,他会生成一对密钥,这一对密钥中如果我们称其中一个叫做公开钥匙,简称公钥,那么另一个就叫做私有钥匙,简称私钥。

公钥和私钥有什么特性呢,就是同一份命名文档如果用公钥加密了那么只有用对应的私钥才能把它解密,同样道理,如果文档用私钥加密了用公钥才能解密。

听到这里你可能有些迷惑,我们来说一下具体场景,比如说李四他有一对公钥和私钥,那么他就可以把他的公钥发布给大家,比如张三是其中的一个人,他拿到了李四的公钥,那么这个时候的加密操作是怎么做的呢。

比如张三如果想传递一份原始文档给李四,那么张三就拿着李四的公钥对原始文档进行加密,把密文再发送给李四,李四拿他的私钥才能进行解密,其他人用了公钥以后都没有办法进行解密。

 ----------                ----------                  ----------  
|  ------  |  李四的公钥    |  ------  |   李四的私钥     |  ------ | 
|  ------  | -----------> |  -- 密 -- |  ----------->  |  ------ |
|  ------  |    加密       |  ------  |     解密        |  ------ |
 ----------                ----------                  ----------
   原始文档                    加密文档                     原始文档 

公钥和私钥还有第二种用途,就是身份验证,比如现在有一段信息张三用它的私钥进行了加密,然后把密文发给了张三或者任何人,只要张三拿到了李四的公钥,因为公钥本身就是公开的,那么用公钥能够成功的解开这段密文就证明这段密文确实是由李四发出的。

这为我们接下来TLS的密钥交换算法提供了基本的签名保障,以上就是对称加密和非对称加密的基本原理。

SSL证书的公信力是如何保证的

在之前的加密过程中,我们谈到了张三和李四之间通讯,但其实他们有个前提条件,就是李四必须知道张三就是张三,也就是他收到的信息必须是张三发来的。

那么这样一个新的问题在多方通信的过程中必须有一个公信机构,这个公信机构就是CA机构。接下来我们来看一看CA是怎么样颁发证书和把证书过期的。

下面这张图中我们可以看到,最右边的CA就是CA机构,他负责颁发证书,而我们作为一个站点的维护者我们就是证书的订阅人,首先我们必须去申请一个证书,为了申请这个证书我可能要去登记我是谁,我属于什么组织,我想做什么。

到了登记机构再通过CSR发给CA,CA中心通过以后他会去把生成一对公钥和私钥,那么公钥会在CA中保存着,公钥私钥证书订阅人拿到之后就会把它部署到自己的web服务器比如说Nginx,当浏览器访问我们的站点的时候,他会去请求我们的证书Nginx这样的web服务器会把我们的公钥证书发给浏览器,而浏览器需要去验证我们的证书是不是合法和有效的。

CA会把过期的证书放在CRL服务器里,这个服务器会把所有过期的证书形成一条链条所以他的性能非常的差,他又推出了OCSP程序他可以就一个证书去查询他是否过期,所以浏览器是可以直接去查询OCSP响应程序的,但OCSP响应程序性能还不是很高。

所以我们的Nginx会有一个OCSP的开关,当我们打开开关以后会由Nginx主动的去OCSP去查询,这样大量的客户端直接从Nginx就可以获取到证书是否有效。

证书究竟是怎样组成的呢?接下来我们看一下证书有哪几种类型。

第一种叫做域名验证DV证书,也就是说这个证书中他只会去验证我们域名的归属是否正确,比如我们申请证书的时候会发现只要域名指向的服务器是正在申请证书的那台服务器,就可以成功的申请到证书。如果使用其他CA机构颁发的证书可能会去验证注册的邮箱是否正确。

第二种证书叫做组织验证,OV验证证书,组织验证就是在申请证书的时候会去验证我们填写的机构,企业名称是否是正确的,所以OV证书的申请往往需要几天的时间,不像DV证书,基本上是实时就可以获取到了,而OV证书他的价格也要远远高于DV证书,DV证书很多都是免费的。

比OV证书做更严格的严重就是扩展证书,EV证书,因为EV证书做了更严格的验证,所以大部分浏览器会对EV证书显示的非常友好,他会把我们在申请证书时所填写的机构名称在浏览器的地址栏中的最左侧显示出来。

那我们获取这样一个证书是怎样生效的呢?浏览器从安全角度对DV,OV或者EV证书他的效果实际上是一样的。唯一验证的就是他的证书链。接下来我们来看一下他的证书链。

当我们点击网站地址栏中的锁头标志,点击证书,打开证书链的时候,可以发现存在三个级别,目前所有站点的主证书都是由三个证书构成的就是根证书,二级证书和我们的主证书。

那么为什么会形成这样一个三级机构呢,这是因为根证书,他的验证是非常谨慎的,像windows,安卓等操作系统每一年以上才会去更新一次他的根证书库,所以一个新的根证书CA机构是很难快速的加入到操作系统或者浏览器认可的库的。

而大部分浏览器他使用的是操作系统的证书库,可能像firefox这样的浏览器,他会维护自己的根证书库,所以浏览器在验证我们的证书是否有效时,除了验证有没有过期以外,最主要就是在验证根证书是不是有效的,是不是被跟证书库所认可的。

而我们的Nginx在向浏览器发送证书的时候呢需要发送两个证书,并不是三个证书,因为根证书是被操作系统或者浏览器内置的,我们不需要发送。

当Nginx向浏览器发送证书时会首先发送站点的主证书,接着会发送二级证书,浏览器会自动去认证二级证书的签发机构,根证书是不是有效的,到这里我们基本上搞清楚了,浏览器和服务器之间通信时怎样确认对方是我信赖的人。归根结底就是去验证给这个站点办法的根证书他的发行者是不是有效的。

SSL协议握手时Nginx的性能瓶颈在哪里

接下来我们看一下TLS的通信过程,通信过程中,双方主要想完成四个目的。

  1. 验证对方身份

由浏览器像服务器发送一个client hello消息。因为浏览器是非常多样化的,而且浏览器的版本在不停的变更,所以不同的浏览器所支持的安全套件,加密算法都是不同的。所以这一步主要是告诉服务器,支持哪些加密算法。

  1. 对安全套件达成共识

Nginx会有一个自己能够支持的加密算法列表,以及他倾向于使用哪一个加密算法套件,Nginx会选择一套他最喜欢的加密套件发送给客户端,如果我们想复用session,也就是说Nginx打开了session cache,希望在一天内断开链接的客户端不用再次协商密钥,那么在这一步,他可以直接去复用之前的密钥。

所以server hello信息中主要会发送究竟我们选择哪一个安全套件。

  1. 传递并生成密钥

Nginx会把自己的公钥证书发送给浏览器,这个公钥证书中是包含证书链的,所以浏览器可以找到自己的根证书库,去验证证书是否是有效的

  1. 对数据进行加密通讯

服务器会发送server hello done,如果之前我们协商的安全套件,比如说椭圆曲线算法,这时需要把椭圆曲线的参数发送给客户端。以方便我们生成最终进行加密的密钥。客户端需要根据椭圆曲线的公共参数,生成自己的私钥以后再把公钥发送给服务器。

服务器有了自己的私钥,把公钥发送给客户端,可以根据自己的私钥和客户端的私钥,共同生成双方加密的密钥,这是服务器独自做的。

客户端根据服务器发来的公钥和他自己的私钥也可以生成一个密钥。

服务器和客户端各自生成的密钥是相同的,这个是由非对称加密算法保证的。

接着我们可以用用生成的密钥进行数据加密,进行通信,从这个过程中我们可以看到,TLS通信主要在做两件事,第一个是交换密钥,第二个是加密数据,所以最消耗性能的也是这两点。

我们来看一下Nginx怎样去优化他的性能,这里我们主要看他的算法性能,Nginx在握手的时候主要看他的椭圆加密算法和RSA算法的性能。

对于小文件,握手是影响他QPS性能的主要指标,对于大文件而言,我们主要考虑对称加密算法的一个性能比如AES,对称加密算法虽然性能很好,但是对非常大的这样一个文件,我们去测吞吐量的时候也可以看出,相对于其他算法AES的性能还是比较好的。

AES的综合性能,当以小文件为主时主要考验的是Nginx的非对称加密的一个性能,比如说RSA,当我们主要处理大文件时主要考验的是对称加密算法的性能,比如说AES。

那么当我们面对的场景是小文件比较多时,我们可能重点应该优化椭圆曲线算法的一些密码强度,看是不是有所降低,当我们主要面对大的文件处理的时候我们需要考虑AES算法是不是可以替换为更有效的算法,或者把密码强度调得更小一些。

以上我们讨论了TLS加密算法的过程,包括握手中怎样交换密钥,以及使用密钥怎样交换数据,以及Nginx在小文件和大文件场景中性能的表现,接下来我们会将演示一下http网站改造为https网站。

用免费SSL证书实现一个HTTPS站点

之前我们已经谈到了TLS的原理,如果现在我们有一个网站,那么我们的域名访问网站的时候,他不是https的,下面我们演示一下如何生成一个免费的DV证书来使得我们的网站编程https的网站。

首先我们有一个域名yindong.zhiqianduan.com, 他是一个http的网站,我们开始安装工具。

如果你的系统是CentOS,可以使用yum install pthon2-certbot-nginx安装这样的一个python工具就可以做到了。如果你是优班图操作系统,可以用wget工具去下载。

当我们安装好这个工具以后,会提供一个命令叫certbot,当我们后缀加上--nginx的时候他就开始为nginx的conf自动执行响应的配置文件的修改了。通常他会默认修改/usr/local/目录下的nginx配置。我们可以通过--nginx-server-root指定nginx.conf所在的路径。

接下来使用-d指定需要申请证书的域名,比如说yindong.zhiqianduan.com。

certbot --nginx --nginx-server-root=/usr/local/nginx/conf/ -d yindong.zhiqianduan.com

日志表明首先他会去获取一个证书,接下来会等待验证,然后会把这个证书部署到nginx.conf文件中。最后提示我们两个选择,第一不要做任何的重定向,第二做重定向。什么意思呢?就是很多时候我们有了https站点以后我们会希望所有的http明文显示的不安全流量全部用301或者302这样的重定向协议把他转到https站点。

那这里呢我们选择1,不做任何重定向,最后他会提示我们已经成功了。这个时候我们访问站点会发现可以使用https访问了。

接下来我们看下nginx.conf里面做了什么工作,可以看到他在server指令块中增加了443端口,接着他将公钥证书和私钥证书部署好,并把一些通用的参数通过include加入到配置文件中。

我们可以看一下options-ssl-nginx.conf里面有什么逻辑。

因为ssl中最消耗性能是的握手,所以为了降低捂手,增加了一个sessin_cache, 设置了1m,可以为大约4000个链接建立服务。也就是说每个http链接握手建立第一次以后如果断开了再次链接,那么在session_timeout时间以内时是不用进行再次握手的。我们可以复用之前的密钥,那么session_timeout设置了1440m,也就是一天。

ssl_protocols表示https支持哪些版本的TLS协议,ssl_prefer_server_ciphers表示nginx开始决定使用哪些协议与浏览器进行通信,他是通过ssl_ciphers中的安全套件,所有的安全套件以分号分隔,他们是有顺序的,排在前面的会优先被使用。所以说如果浏览器支持所有的安全套件的时候会选用第一个。

最后server中的ssl_dhparam是表示加密的时候使用怎样的参数,这些参数会决定网略安全的加密强度。

以上就是将http站点改造成https站点的一个过程。

基于OpenResty用Lua语言实现简单服务

之前我们介绍了官方开源Nginx的安装和实践,其实很多人在使用openresty做一些开发。下面我们演示一下openresty,这里我们包含五个部分。

  1. 下载openresty

  2. 分析目录结构

  3. 执行编译,安装

  4. 添加lua代码

  5. 运行openresty,看下添加lua代码的显示效果。

首先我们到openresty的站点,openresty.org, 这个时候我们去下载,下载中通常不会用二进制版本,在源码发布中找到最新版本。复制他的下载链接进行下载。