| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
kamran
8年前发布

TLS/SSL 高级进阶

   <p>TLS/SLL 是现在网络安全通信比较重要的一环,通过一些列的 key 交换和 key 生成,最终确立加密通道的整个流程。众所周知,TLS/SSL 耗费的时间也是挺可观的,相对于 TCP 的3次 RTT 来说,如果加上 TLS/SSL, 则总的 RTT 时间至少为 4 次。虽然看起来很多,但如果相对于现在的网络环境来说,大概也就每次 20~30ms,这样算下来,估计也就 100ms 左右。这样的时间差还是可以忍受的,不过,这里还没算入 DNS 解析,这个暂时不考虑。而且,TLS/SSL 生成的 sessionkey 如果在有效期内的话,那么这个时间就可以完全忽略掉了。不多说了,我们直接来看下 TLS/SSL 的基本内容。</p>    <h2><strong>TLS 算法</strong></h2>    <p>TLS/SSL 其实就是通过非对称加密,生成对称加密的 sessionkey 的过程。对称加密的算法无外乎就 AES,或者使用 Cipher/Decipher 模块。而非对称加密常用的就是 RSA,不过也有使用 Diffie-Hellman (迪菲)。但,较安全性来说,DH 要高一些。因为,RSA 在生成 sessionkey 时,最后是由 browser 生成,然后通过 public key 加密后传给 server 的,这样存在一定的问题是,如果 hacker 得到了 private key,那么,他可以全程监控流量,然后使用 private key 进行解密,那么可想而知,sessionkey 也就暴露了。</p>    <p>但对于 DH 来说,它机制的不同点在于,sessionkey 不会通过网络传输,而是在两端独立生成的。ok~ 这就涉及到两个 key 的一致性问题。DH 还有一个机制,即,前向安全性(perfect forward secrecy--PFS):server 端的 private key,不能用来代替以前任何一把的 sessionkey,所以,也无法破解以前任何一次的 session 内容。每次连接,DH 都会重新生成一个 key,并且当该次 session 结束时,丢弃它。不过,这并不是很大的问题,因为 DH 能很快的生成 key。因为它耗费在网络上的时间相比于 RSA 来说少了一半。可以从下图简单了解 <a href="/misc/goto?guid=4959722520466595126" rel="nofollow,noindex">DH</a> 的加密算法:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/3ee92bd585d099a2376e316f21eafe1e.png"></p>    <p>简单来说,两边通过一次的信息交换,完成了密钥生成。因为 sessionkey 是独立放在两端的,为了达到一致性,每次连接时,DH 都需要重新协商生成 sessionkey。现在有个问题是: 为什么一定要有 session key,他存在的意义是什么?</p>    <h3><strong>sessionkey 用途</strong></h3>    <p>TLS/SSL 其实就是通过非对称加密,生成对称加密的 session key 的过程</p>    <p>那 session key 主要又干了什么呢?</p>    <p>首先,我们要先明确,session key 是用来进行对称加密的,这种加密方式主要使用到的是 AES 加密算法。这不同于 Elliptic Curve Diffie-Hellman (ECDH) 这种非对称加密算法。两者其实都可以用来对信息进行加密,但由于算法内部的实现机理不同,他们所用的时间也是不一样的。基本上是,ECDH 所用的时间是 AES 的 3 倍。</p>    <p>你可以自行测试一下:</p>    <pre>  openssl speed ecdh  openssl speed aes</pre>    <p>ok,上面大概简述了 TLS/SSL 所用到的算法。接下来,我们来了解一下,具体 TLS/SSL 密钥交换的过程。</p>    <h2><strong>TLS/SLL 过程</strong></h2>    <p>在详述过程之前,我们需要了解一下,在过程中会出现的内容。</p>    <ul>     <li> <p>session key: 这是 TLS/SSL 最后协商的结果,用来进行对称加密。</p> </li>     <li> <p>client random: 是一个 32B 的序列值,每次连接来时,都会动态生成,即,每次连接生成的值都不会一样。因为,他包含了 4B 的时间戳和 28B 的随机数。</p> </li>     <li> <p>server random: 和 client random 一样,只是是由 server 端生成。</p> </li>     <li> <p>premaster secret: 这是 48B 的 blob 数据。它能和 client & server random 通过 pseudorandom (PRF) 一起生成 session key。</p> </li>     <li> <p>cipher suite: 用来定义 TLS 连接用到的算法。通常有 4 部分:</p>      <ul>       <li> <p>非对称加密 (ECDH 或 RSA)</p> </li>       <li> <p>证书验证 (证书的类型)</p> </li>       <li> <p>保密性 (对称加密算法)</p> </li>       <li> <p>数据完整性 (产生 hash 的函数)</p> <p>比如 AES128-SHA 代表着:</p>        <ul>         <li> <p>RSA 算法进行非对称加密</p> </li>         <li> <p>RSA 进行证书验证</p> </li>         <li> <p>128bit AES 对称加密</p> </li>         <li> <p>160bit SHA 数据加密算法</p> </li>        </ul> </li>       <li> <p>比如 ECDHE-ECDSA-AES256-GCM-SHA384 代表着</p>        <ul>         <li> <p>ECDHE 算法进行非对称加密</p> </li>         <li> <p>ECDSA 进行证书验证</p> </li>         <li> <p>265bit AES 对称加密</p> </li>         <li> <p>384bit SHA 数据加密算法</p> </li>        </ul> </li>      </ul> </li>    </ul>    <p>相信大家对这张图已经很熟悉了:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/fb45d0a999dc6a4615f038e6af543166.png"></p>    <p>不过,上面不是说了有两种不同的非对称加密方式吗? RSA & DH? 那为什么图只是一个嘞?</p>    <p>ok,实际上这图没错,他并没有把传输的内容是什么写出来,这很关键。而两种算法也是在传输内容上区分开的,基本的过程是完全一样的。根据 <a href="/misc/goto?guid=4959722520565100949" rel="nofollow,noindex">wiki</a> 的解释,我们大概能知道整个传输过程需要的内容。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/ab4713f4267dd5891ae11b1973e02332.png"></p>    <ol>     <li> <p>客户端发送 clientHello 信息,包含了客户端支持的最高 TLS 协议版本,random num (上文提到过),cipher suite。如果,客户端使用 resumed handshake,那么这里发送的就是 sessionID。如果,客户端还支持 ALPN,那么它应该还需要发送它所支持的其他协议,比如 HTTP/2.</p> </li>     <li> <p>在 server 端进行 serverhello 阶段,这里 server 根据 client 发送过来的相关信息,采取不同的策略,同样会发送和 client 端匹配的 TLS 最高版本信息,cipher suite 和 自己产生的 random num. 并且,这里会产生该次连接独一无二的 <strong>sessionID</strong> 。</p> </li>     <li> <p>通过 certificate 阶段,会在信息流中加入 public key certification。ServerKeyExchange 该阶段,主要是针对于 ECDH 加密方式,这里就不赘述,后面再进行讲解。</p> </li>     <li> <p>serverHelloDone 标识这 server 阶段处理结束,将该阶段产生的信息发送给 client。</p> </li>     <li> <p>在 clientKeyExchange 阶段时,client 会随机生成一串 pre-master secret 序列,并且会经由 public key 加密,然后发送给 server。在 ChangeCipherSpec 阶段,主要是 client 自己,通过 pre-master secret + server random-num + client random-num 生成 sessionKey。这就标识着,此时在 client 端,TLS/SSL 过程已经接近尾声。</p> </li>     <li> <p>后面在 server 端进行的 ChangeCipherSpec 和 client 进行的差不多,通过使用 private key 解密 client 传过来的 pre-master secret ,然后生成 sessionkey。 后面再通过一次验证,使用 session Key 加密 server finshed ,发送给 client,观察能够成功解密,来表示最终的 TLS/SSL 完成。</p> </li>    </ol>    <p>上面主要是根据 RSA 加密方式来讲解的。因为 RSA 才会在 TLS/SSL 过程中,将 pre-master secret 显示的进行传输,这样的结果有可能造成,hacker 拿到了 private key 那么他也可以生成一模一样的 sessionKey。即,该次连接的安全性就没了。</p>    <p>接下来,我们主要讲解一下另外一种加密方式 DH。它和 RSA 的主要区别就是,到底传不传 pre-master secret。RSA 传而 DH 不传。</p>    <p>根据 cloudflare 的讲解可以清楚的了解到两者的区别:</p>    <p>这是 RSA 的传输方式,基本过程如上述。</p>    <p><img src="https://simg.open-open.com/show/0c8452265ea781fa01b487dd7f6e3adc.jpg"></p>    <p>而 DH 具体区别在下图:</p>    <p><img src="https://simg.open-open.com/show/4c87e8f55b518939a146b32cd900beb1.jpg"></p>    <p>这里先补充一下 DH 算法的知识。因为,pre-master secret 就是根据这个生成的。DH 基本过程也不算太难,详情可以参考 <a href="/misc/goto?guid=4959722520650939794" rel="nofollow,noindex">wiki</a> 。 它主要运用到的公式就是:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/63b6e891a3ae82e00e168d3df99ac5db.png"></p>    <p>为了防止在 DH 参数交换时,数据过大,DH 使用的是取模数的方式,这样就能限制传输的值永远在 [1,p-1]。这里,先说明一下 DH 算法的基本条件:</p>    <ul>     <li> <p>公共条件: p 和 g 都是已知,并且公开。即,第三方也可以随便获取到。</p> </li>     <li> <p>私有条件: a 和 b 是两端自己生成的,第三方获取不到。</p> </li>    </ul>    <p>基本流程就是:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/c0197243626f7002735a856167db050a.png"></p>    <p>我们只要把上图的 DH parameter 替换为相对应的 X/Y 即可。而最后的 Z 就是我们想要的 Premaster secret。 之后,就和 RSA 加密算法一致,加上两边的 random-num 生成 sessionKey。通过,我们常常称 DH 也叫作 Ephemeral Diffie-Hellman handshake 。 因为,他每次一的 sessionKey 都是不同的。</p>    <p>而 RSA 和 DH 两者之间的具体的区别就在于:RSA 会将 premaster secret 显示的传输,这样有可能会造成私钥泄露引起的安全问题。而 DH 不会将 premaster secret 显示的传输。</p>    <h2><strong>TLS/SSL 中的基本概念</strong></h2>    <p>上面内容大概讲清楚了基本的 TLS/SSL 的加密过程。不过,其中,还有很多其他的小细节,比如 SNI,ALPN,Forward Secrey。 接下来,我们主要将这些细节将一下,因为他们其实也很重要。</p>    <h3><strong>Forward Secrey</strong></h3>    <p>FS(Forward Secrey) 主要是针对 private key 进行描述了。如果你的 private key 能够用来破解以前通信的 session 内容,比如,通过 private key 破解你的 premaster secret ,得到了 sessionKey,就可以解密传输内容了。这种情况就是 non-forward-secrey。那如何做到 FS 呢? 很简单,上文也已经提到过了,使用 DH 加密方式即可。因为,最后生成的 sessionKey 和 private key 并没有直接关系,premaster secret 是通过 g(ab) mod P 得到的。</p>    <p>简单的说就是,如果你想要启用 FS,那么你应该使用的是 DH 加密方式,而放弃 RSA。不过,由于历史原因(TLS 版本问题),RSA 现在还算是主流的加密方式。但,DH 也凭借他 5S 的安全性,份额也在增加。</p>    <h3><strong>ALPN</strong></h3>    <p>ALPN 全称是 Application Layer Protocol Negotiation(应用层协议协商机制)。看到应用层,程序员们应该都能反应出 OSI 7层网络协议。在应用层中,HTTP 协议应该是重点。不过,由于 HTTP 版本的问题,以及现在 HTTP2 的流行,为了让 client-server 使用相同的协议而出现了 ALPN。ALPN 实际上是从 SPDY 中的 NPN 协议衍生出来的。不过,它们俩的机制正好相反。</p>    <ul>     <li> <p>NPN: 由 server 端告诉 client,它支持什么协议,然后 client 确认支持的协议后,开始进行连接。</p> </li>     <li> <p>ALPN:在 TLS 阶段,由 client 告诉 server,它所支持的所有协议,然后开始进行连接。</p> </li>    </ul>    <p>总的来说,NPN 已经退出历史的舞台了。。。ALPN 现在是 IETF 指定的标准协议。ALPN 在 TLS 具体的过程是:</p>    <ul>     <li> <p>在 clientHello 阶段,client 会在 message 中,添加一个 ProtocolNameList 字段。用来表示它所支持的协议列表</p> </li>     <li> <p>server 端在 serverHello 阶段,处理 client 提供的 ProtocolNameList。并且选择最高版本的协议,执行。将选择信息添加到 serverhello 内。</p> </li>    </ul>    <h3><strong>SNI</strong></h3>    <p>SNI 的全称为:Server Name Indication。该机制的提出的意义是,当有一个 server 同时处理了很多个 host 时。相当于,一个 IP 映射多个域名,但,由于证书只能对一个 3 级域名有效,所以,针对于多个 host 来说,server 为了能同时兼顾这些域名。一种简单的办法就是重定向到指定域名,如果都想支持的话,也行,掏钱自己多买几个证书 (真土豪)。如果,你很土豪的话,现在就有这样的情况,一个 IP 服务器下,搭载了支持多个域名的 server,并且每个域名都有合法的 CA 证书。那么,server 怎么判断,哪一个域名用哪一个证书呢?这时候,就需要用到 SNI。相当于在 TLS 阶段,将 host 一并发送过去,然后 server 就知道在 serverhello 阶段该返回啥证书了。</p>    <p>现在,有个问题,为什么一定要用 SNI 呢?</p>    <p>我们回想一下,这里我们仅仅只是建立 TCP + TLS 连接,客户端的一些内容,比如 hostname,我们并不能在 TCP 中获得。而,想要获得的话,就需要等到 HTTP 阶段,获得 client 传过来的 host 或 origin 字段。所以,为了解决这个比较尴尬的点,就提出了 SNI。</p>    <h2><strong>Session Resumption</strong></h2>    <p>感觉能看到这里的人,应该都是闲的蛋疼的人。。。如果让我来看这篇文章,估计看几张图,我基本上就直接关网站了。因为,这实在是复杂。并且,上面说的只是协议上的复杂性,对于计算机来说,只需要记下每一次该发什么东西而已,但真正让 Computer 感到蛋疼的是,key 的计算。特别是 random key 和 premaster secret 动不动就是 32B,48B 的数据量。所以,为了真正减少计算机的工作量(实际上是 server),提出了 Session ID 和 Session Tickets,来将成功进行连接的 session 内容缓存起来。</p>    <h3><strong>Session ID</strong></h3>    <p>Session ID 是 server 将上一次成功连接的 session 内容存在自己的硬盘里面。这里,就不涉及对 session data 的二次加密。基本的过程是:</p>    <ol>     <li> <p>client 端在 clientHello 阶段,将 random num,TLS protocol 和通过 hostname 匹配到的最新一次的 session ID 发送给 server 端。(也就是说,client 同样需要存储一份 session data)</p> </li>     <li> <p>server 接收到 session ID 后,在缓存中查找,如果找到,则直接进行 ChangeCipher 阶段,开始生成sessionKey。然后,返回相同的 sessionID 即可。</p> </li>    </ol>    <p>那么相对于完全的 TLS/SSL 连接来说,这里只用到了一次 RTT。那么协议过程就变为了:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/db51241cfb94b1dcc21753459b1fa0b8.png"></p>    <h3><strong>Session Ticket</strong></h3>    <p>既然 Session ID 是为了解决网络时延和计算机性能问题,那么 Session Ticket 又干了什么呢?</p>    <p>Session Ticket 和 Session ID 做的也是同样的事情,server 将最新一次的 sesion data 通过二次加密,在上一次握手结束时传递过去,然后 client 将传递过来的信息保存。</p>    <p>这样,利不利用缓存的 session data 这时,就取决于 client。如果该次的 session data 没有过期,那么 client 就会在 clientHello 阶段将该数据发送过去,server 接受到之后,便开始进行解密然后,双方生成 sessionKey,握手结束。</p>    <p>那 Session Ticket 和 Session ID 到底用哪一个呢?</p>    <p>这估计得看你的业务情况了,Session ID 注重的是节省性能,而损耗部分空间。Session Ticket 注重的是节省空间,而损耗部分性能。它们两者都能节省一次 RTT 时间,用谁,还是得看你的服务器的具体情况。</p>    <h2><strong>CA 证书详情</strong></h2>    <p>前面大致说了 TLS/SSL 是怎样运作的,以及有哪些连接方法。相当于,学画一条线一样,我们现在只知道这条线该画多长,但还不知道,这条线从哪里画。所以,接下来,我们就需要来探讨一下,两端发生了什么。其实也不难,主要还是关于 CA 证书的存放和验证。server 端的很简单,就是把自己的 CA 证书发过来就 ok。但,client 验证这个证书是否可信,会有点复杂。</p>    <p>首先,证书颁发机构就那么一些,换句话理解就是,每个证书颁发机构,就代表着一张 CA 证书。但,现在市面上的 HTTPS 网站,辣么辣么多,难道他们都用同一张证书?难道他们都有一样的 pu/pr key? 那么 HTTPS 安全还有用吗?</p>    <p>所以,按照上面的推理,我们的网站上的 HTTPS 证书,肯定都是各不一样的。一般来说,有 3 种类型的证书: DV(Domain Validation),OV(Organization Validation),EV(Extended Validation)。均价按照顺序上升,所以,最便宜的就是 DV,这应该是我们勤劳的贫苦大众用得起的。它们之间具体的区别在于域名的支持上:</p>    <ul>     <li> <p>DV:就是个人证书嘛,基本支持的就是单域名和多域名,不支持泛域名(*.villainhr.com)。不过,看价格,比如我这个就是腾讯云给的一个免费的 DV 证书,所以,就支持一个 3 级域名。如果是收费的,单/多域名应该都支持。</p> </li>     <li> <p>OV:就比较牛逼,面向企业的,多域名/泛域名都支持。</p> </li>     <li> <p>EV:属于贵族用的,一般人也搞不到,主要它还需要去买个保险。。。</p> </li>    </ul>    <p>那我们的证书在芸芸证书中,是处于哪一个层级呢?</p>    <p>一般是三级。怎么体现的呢?</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/d55f24f6ee2c91e8704131255c84b564.png"></p>    <p>那这么多证书,我们用的是哪一个呢?当然是,最下面那个。因为每个证书并不是都被信任,所以客户端首先就要了解一下,你这个证书能否用来进行验证。如果不行的话,那么你这次连接就是不被信任的,就没有 绿色的小锁 。这就需要了解一下,客户端的验证过程。</p>    <h3><strong>CA 链式验证</strong></h3>    <p>首先,什么叫做可信的证书呢?</p>    <p>我们先要明白一个道理,HTTPS 是先建立在人与人之间的相互信任上,然后才建立在机器与机器的相互信任上。假如,根证书 A 机构,恶意的将一个以前颁发过的证书,又给了另外一个 不要脸 拦截站点(比如,用来插广告的)。这样,我拿到了这个证书后,就可以自己搭一个服务器,用来进行拦截浏览,监管里你网站,并强行插广告。这就被称为不可信的机构/证书。</p>    <p>而验证的可行性,通常又跟机构的权威性有着极大的关系。它基本的验证过程简述就是(按照上面的层级):</p>    <ul>     <li> <p>www.villainhr.com 问 TrustAsia DV SSL,我的证书可不可信?</p> </li>     <li> <p>可信!ok,继续。TrustAsia DV SSL 问 VeriSign Class 3,我的证书可不可信</p> </li>     <li> <p>可信!ok,然后便开始 TLS/SSL 连接。</p> </li>    </ul>    <p>如果上述任一步骤出现问题,那么该次 TLS/SSL 就不会进行,会回退。那么它们在询问的时候,会不会发送网络请求呢?</p>    <p>不会~ 因为,电脑在初始化时,会自带很多可信任的证书机构(即,Root CA),也就是我们刚刚提到的 VeriSign Class 3 的证书机构。以及,能够签发证书的二级机构(比较少)。到时候,浏览器会自动的根据数字签名来进行证书的验证。</p>    <h3><strong>CA 合法验证</strong></h3>    <p>上面已经阐述了,CA 证书的合法性是自下而上的验证方式。那么它们具体验证协议是怎样的呢?</p>    <p>在说之前,我们先说几个概念:</p>    <ul>     <li> <p>数字签名:它是用颁发机构的私钥,对下级证书的公钥进行加密生成的值。digital_sign = CA_pr_key + sub_Cer_ppu_key。</p> </li>     <li> <p>解密:用颁发机构的公钥对数字签名进行解密,对比下级证书的公钥和解密后的值是否一致。</p> </li>    </ul>    <p>CA 验证首先需要说一下它的颁发过程:</p>    <ul>     <li> <p>颁发机构 A,用自己的私钥将需要生成的下级证书 B 的公钥进行加密,生成数字签名,然后再带上相关信息:公钥,公钥的指纹,数字签名,证书名,签发机构等。</p> </li>    </ul>    <p>然后,验证过程就是根据这个来的:</p>    <ul>     <li> <p>浏览器解析下级证书 B 的相关信息,找到签发机构和数字签名。</p> </li>     <li> <p>然后,找到签发机构 A,使用 A 的公钥去解密数字签名,然后对比下级证书 B 的公钥。如果成功则合法,反之,不合法。</p> </li>    </ul>    <p>而上面的三级证书层级,也是同样的道理,自上而下的找就 ok。当然,有时候为了验证的速度,会做一些缓存,这样就不必再进行验证了。所以,根据上面的描述,有童鞋可能会想到,能不能自签证书呢?反正,浏览器也是从本地找的。</p>    <p>当然可以, openssl 就可以生成你自己的 CA。不过需要注意的是,你生成的 CA 只是在你自己的电脑上使用,如果你想保证你的 CA 在其他电脑上也能使用的话(这是不可能的),那就用钱砸就 ok。</p>    <p>以前,在使用 Charles 和 Fiddler 的时候,一直在想,它们是怎么做到,将自己的证书,变成签发机构证书。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/1bf12ca5baa895bf55529d2d9958966c.png"></p>    <p>后来发现,它是把证书中的相关字段该成它的证书内容。不过,对于某些高级证书,还是会有一些问题,比如,wx.qq.com(微信的)</p>    <p>另外,为了证书的可靠性,提出了 Certificate Transparency 项目,实际上,就是让证书机构公开它的签发流水。防止出现重复签发。</p>    <h3><strong>证书的吊销</strong></h3>    <p>现在有个问题,为什么证书有过期时间呢?</p>    <p>这同样是为了安全性,前面说过,如果你的证书发生了泄漏(实际上就是私钥)。那么,其他服务器就可以作为一个代理去拦截你的流量。这时候,由于过期的原因,可能一段时间后,中间恶意的服务器就没用了,另外,如果你发现了你遗失了证书,可以向颁发机构去挂失。</p>    <p>另外,还有一个原因是证书吊销的 CRL 机制。简单来说,就是有一个列表来记录当前时间,该颁发机构被吊销的证书 list。如果,没有过期时间的话,那么这个 list,会随时间程指数增长,引入过期机制的话,该 list 只要记录当前没过期但吊销的证书信息即可。</p>    <p>证书的吊销有两种机制:CRL,OCSP</p>    <p><strong>CRL</strong></p>    <p>CRL(Certificate Revocation List),即,证书吊销列表。CA 机构会生成一个列表,列表里面是当前周期被吊销证书的序列号,当进行证书验证时,同样也会进行验证该项。如果,已经是吊销证书的话,那么该次 TLS/SSL 连接也会失败。</p>    <p>我们可以从证书信息中找到 CRL URI:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/843adae1c5f144dbbe032838975c5b80.png"></p>    <p>该协议虽然简单,但,缺陷还是比较多的。</p>    <ul>     <li> <p>下载时间。因为该 list 不是自带的,需要从颁发机构下载,这就造成了网络时延。</p> </li>     <li> <p>缓存时间。如果存在缓存,就存在了信息不同步的问题,如果一个证书已经过期,但缓存中显示的是未过期,那么也是一个安全问题。</p> </li>    </ul>    <p><strong>OCSP</strong></p>    <p>OCSP(Online Certificate Status Protocol),即,在线证书状态协议。它通过在线请求的方式来进行验证,不需要下载整个 list,只需要将该证书的序列号发送给 CA 进行验证。当然,验证通过也会有一定的缓存期。不过,由于验证也会存在时延。另外,部署 OCSP 对 CA 也有一定的要求,CA 要搭建的一个服务器来接受验证,并且,该服务器的性能要好(负载很大)。</p>    <p><strong>OCSP stapling</strong></p>    <p>OCSP stapling 常称为: TLS Certificate Status Request extension。是 OCSP 的另外一种实现方式,因为前两个(OCSP,CRL)都是由客户端去验证证书是否吊销,并且都会发送请求。而 OCSPs(OCSP stapling)则是直接在 server 端,进行证书的有效性验证。server 会周期性的向 CA 机构发送请求,验证有效性,并在 certificate 阶段,发送相应的签名信息。不过,该协议是建立在,我们完全信任 serve 的情况下,这里就排除了一些恶意的中间服务器。</p>    <h2><strong>TLS/SSL 优化</strong></h2>    <p>TLS/SSL 主要的性能调优简单包括:启用 False Start, OSCP Stapling, 选择合适 cipher suite, resumption 等。另外,如果你追求 fashion , 那么 HTTP/2 应该是个不错的选择。</p>    <p>想要做 TLS/SSL 优化,那么你必须了解,TLS/SSL 握手的整个过程是什么。当然,你可以买个证书,从头自己搭建一个服务器,但是,这样只能证明 你很有钱 外,其它也证明不了什么。因为,这完全可以自己内网搭一个呀。这里,我们结合 nginx 来具体对 TLS/SSL handshake 优化,做个整体的阐述。</p>    <h3><strong>设置 session 缓存</strong></h3>    <p>session 缓存设置可以让两次的 RTT,变为一次,这相当于快了一倍(不包括,密钥计算等)。不同的 server 设置 session 的办法有很多,这里以 nginx 为例。在 nginx 中,支持的是 Session ID 的形式,即在 server 中缓存以前 session 的加密内容。涉及的字段有两个, ssl_session_cache 和 ssl_session_timeout 。</p>    <ul>     <li> <p>ssl_session_cache:用来设置 session cache 上限值,以及是否在多个 worker 之间共享</p> </li>     <li> <p>ssl_session_timeout:用来设置 session cache 存储的时间</p> </li>    </ul>    <p>看个 demo 吧:</p>    <pre>  ssl_session_cache shared:SSL:10m;  ssl_session_timeout 20m;</pre>    <p>表示的意思是:session cahce 会在不同的 worker 之间分享,假设 1MB 只能存储 8000 次握手的信息。那么, 10 MB 一共可以存储 80000 次握手信息。如果超出,则不会存储。缓存信息存在时长为 20分钟。</p>    <p>另外,你也可以开启 session ticket 。ST(session ticket) 需要一个sign 参数,使用 openssl 创建即可。</p>    <pre>  $ openssl rand 48 > ticket.key  # 在 nginx 中开启  ssl_session_tickets on;   ssl_session_ticket_key ticket.key;</pre>    <h3><strong>选择合适的 cipher suite</strong></h3>    <p>这里先声明一下,你的证书的内容和你的加密套件实际没有半毛钱关系,这主要还是取决于你的服务器的支持程度以及客户端的支持度。另外,如果你想启用 False Start ,这也可套件的选择有很大的关系。我们来看一下如果设置吧。在 nginx 中,主要用到两个指令:</p>    <pre>  ssl_prefer_server_ciphers on;  ssl_ciphers xxx;</pre>    <ul>     <li> <p>ssl_prefer_server_ciphers: 用来告诉客户端,要按照我提供的加密套件选择。</p> </li>     <li> <p>ssl_ciphers: 具体设置的加密套件内容,使用 : 分隔。</p> </li>    </ul>    <p>支持性最高的就是使用:</p>    <pre>  // 让浏览器来决定使用哪一个套件(额。。。最后的手段)  ssl_ciphers  HIGH:!aNULL:!MD5;</pre>    <p>一般情况,还是应该自己来决定使用哪一个套件,这样安不安全由自己说了算。具体可以参考 <a href="/misc/goto?guid=4959656370022006864" rel="nofollow,noindex">mozilla 的套件配置</a> 。这里简单放一个,比较安全的,下面所有的套件都必须支持 Forwar Secrecy</p>    <pre>  ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5</pre>    <p>不过,以下的加密套件,最好不要使用,因为基本上都不安全:</p>    <ul>     <li> <p>aNULL: 是一种非标准的 DH 密钥交换套件。容易被 中间人 攻击。</p> </li>     <li> <p>eNULL: 没有加密方式,明文交换</p> </li>     <li> <p>EXPORT: 一种弱加密方式,老美那边早期使用的</p> </li>     <li> <p>RC4: 使用已经废弃的 ARCFOUR 算法</p> </li>     <li> <p>DES: 使用已经废弃的 Data Encryption 标准</p> </li>     <li> <p>SSLv2: 老版本 SSL2.0 的加密套件(最少,你也写 SSLv3 嘛)</p> </li>     <li> <p>MD5: 直接使用 MD5 加密方式</p> </li>    </ul>    <p>上面那些只能给一些远古浏览器使用,基本上在选择中是作为垫底的选择。</p>    <p><strong>False Start</strong></p>    <p>另外,怎么在 nginx 中开启 False Start 呢? 这其实和服务器并没有多大的关系,关键还是你选择的套件和 NPN/ALPN 协议的作用。</p>    <ul>     <li> <p>首先,你的加密套件必须具有 Forward Secrecy,否则开不了。</p> </li>     <li> <p>浏览器需要使用 NPN 或者 ALPN 告诉服务器,该所需的协议版本,然后再决定开不开启。</p> </li>    </ul>    <p>那么,在 nginx 中,我们只要选择好合适的加密套件即可。这里就放一份现成的吧</p>    <pre>  ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256::DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5';</pre>    <h3><strong>使用 DH 密钥交换</strong></h3>    <p>DH 的加密过程,上面已经说过了。DH 自带两个公共的参数,所以,这必须手动进行创建(实际上就是将参数 sign 一遍)。</p>    <pre>  // 创建一个 DH param  openssl dhparam 2048 -out dhparam.pem</pre>    <p>然后,调用该文件</p>    <pre>  ssl_dhparam dhparam.pem;</pre>    <p>这样,你就正式的开启的 DH 加密模式。如果你使用抓包工具观察一下,此时 DH 应该会在 Server Hello 里:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/6686aa188b1b9707cab8455b756e4178.png"></p>    <p>不过,由于历史原因,DH param 已经使用的长度是 1024,比如: 采用 Oakley group 2 版本。现在,比较流行的 DH 加密方式是 ECDHE ,它和以前的加密方式( DHE )比起来,在密钥生成这块会快很多。同样,由于历史原因,它的基本条件比较高:(其实也还好)</p>    <ul>     <li> <p>Android > 3.0.0</p> </li>     <li> <p>Java > 7</p> </li>     <li> <p>OpenSSL > 1.0.0</p> </li>    </ul>    <h3><strong>开启 OCSP Stapling</strong></h3>    <p>OCSP Stapling 是验证证书权威性的一种手段,前面还有两种 CRL 和 OCSP。不过,它们都是让 client 自己去验证。而 OCSP Stapling 则把验证这块放到了 server 里,通过定期检查,来减少网络时间中的消耗。要开启 OCSP Stapling 首先是需要你证书的 chain 文件,该是用来详细说明,从根证书到你的证书中间所要经历的所有验证(和其他两种验证手段一样)。那如何得到 chain 文件呢?直接去问你的证书颁发机构,这个又不是啥秘密文件。如果是自发证书(自己测试用的),那就自己生成。将所有的中间证书按照 bottom to up 放到一个文件里:</p>    <pre>  cat intermediate/certs/intermediate.cert.pem \        certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem;</pre>    <p>那么 ca-chain.cert.pem 就是 OSCP stapling 验证文件。然后在 nginx 开启即可。</p>    <pre>  ssl_stapling on;  ssl_stapling_verify on;  ssl_trusted_certificate ca-chain.cert.pem;  resolver 8.8.8.8 8.8.4.4; // 默认使用 Google 的</pre>    <p>关于 DNS 解析,同样你也需要问一下证书提供商,当然,该值可以不用管。下面也同样适用</p>    <pre>  ssl_stapling on;  ssl_stapling_verify on;  ssl_trusted_certificate ca-chain.cert.pem;</pre>    <p>开启过后,你可以使用 openssl s_client -connect www.yourDomainName.com:443 来测试一下,检测是否开启成功。</p>    <h3><strong>开启 HSTS</strong></h3>    <p>HSTS(HTTP Strict Transport Security) 实际上就是一个响应头,没啥很特别的,具体内容就是,你所有对外部的请求都是 https ,所以这有一个问题,如果你的图片地址是 http 的,那么最终的结果,会变为 https://xxx ,有可能会造成资源丢失的情况,所以,开不开启还需要慎用。</p>    <pre>  Strict-Transport-Security: max-age=15768000 # 设定 6 个月的强制期</pre>    <p>在有效时间内,客户端都会尝试使用 https 访问你的站点,如果在这期限里你的证书过期了,开不了 https 。那么,呵呵。</p>    <h3><strong>使用 SNI</strong></h3>    <p>SNI 就是针对一个 IP 手握很多张证书时,用到的协议机制,这主要是用来区分,不同的 host,使用不同的证书。SNI 详情上面已经说过了,这里就不赘述了。主要使用格式就是不同的 server_name 搭配不同的 certificate</p>    <pre>  server{      server_name www.abc.com;      ssl_certificate abc.crt;      ssl_certificate_key abc.crt.key;  }  server{      server_name www.def.com;      ssl_certificate def.crt;      ssl_certificate_key def.crt.key;  }</pre>    <p>如何开启呢?换个高版本的 nginx 就行了。你可以使用 nginx -V 检查你的 nginx 是否带有</p>    <pre>  TLS SNI support enabled</pre>    <h3><strong>完整示例</strong></h3>    <p>最后,放一份完整的吧:</p>    <pre>  server {          listen 443 ssl http2; # 默认打开 http2          listen [::]:443 ssl http2;            ssl_certificate /etc/nginx/cert/bjornjohansen.no.certchain.crt;          ssl_certificate_key /etc/nginx/cert/bjornjohansen.no.key;            ssl_session_cache shared:SSL:10m;          ssl_session_timeout 20m;            ssl_prefer_server_ciphers on;            ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;            ssl_dhparam /etc/nginx/cert/dhparam.pem;            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;            ssl_stapling on;          ssl_stapling_verify on;          ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;          resolver 8.8.8.8 8.8.4.4; # 看情况选择 DNS IP            #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;          # 我一般不开 HSTS          # add_header Strict-Transport-Security "max-age=31536000" always;  }</pre>    <h3><strong>参考列表</strong></h3>    <p>这个,配置也很简单,你可以从 Mozilla 里获得更丰富的内容。详情可以参考:</p>    <ul>     <li> <p><a href="/misc/goto?guid=4959722520767651550" rel="nofollow,noindex">https://wiki.mozilla.org/Secu...</a></p> </li>     <li> <p><a href="/misc/goto?guid=4958824622611440285" rel="nofollow,noindex">https://wiki.mozilla.org/Secu...</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959722520877198284" rel="nofollow,noindex">https://hpbn.co/transport-lay...</a></p> </li>    </ul>    <p>另外,还有一些测试工具和生成工具,这里也提供一份 list:</p>    <ul>     <li> <p><a href="/misc/goto?guid=4959722520969557229" rel="nofollow,noindex">摸直了 官方测试工具</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959671354483924074" rel="nofollow,noindex">摸直了 官方 tls/ssl 配置生成工具</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959722521082496281" rel="nofollow,noindex">SSL lab tls/ssl 测试工具</a></p> </li>    </ul>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000007283514</p>    <p> </p>    
 本文由用户 kamran 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1477548899809.html
SSL协议 加密解密 加密/解密软件包