简单来说,两边通过一次的信息交换,完成了密钥生成。因为sessionkey是独立放在两端的,为了达到一致性,每次连接时,DH都需要重新协商生成sessionkey。现在有个问题是:为什么一定要有sessionkey,他存在的意义是什么?
TLS/SSL其实就是通过非对称加密,生成对称加密的sessionkey的过程
你可以自行测试一下:
opensslspeedecdhopensslspeedaesok,上面大概简述了TLS/SSL所用到的算法。接下来,我们来了解一下,具体TLS/SSL密钥交换的过程。
在详述过程之前,我们需要了解一下,在过程中会出现的内容。
相信大家对这张图已经很熟悉了:
上面主要是根据RSA加密方式来讲解的。因为RSA才会在TLS/SSL过程中,将pre-mastersecret显示的进行传输,这样的结果有可能造成,hacker拿到了privatekey那么他也可以生成一模一样的sessionKey。即,该次连接的安全性就没了。
接下来,我们主要讲解一下另外一种加密方式DH。它和RSA的主要区别就是,到底传不传pre-mastersecret。RSA传而DH不传。
这是RSA的传输方式,基本过程如上述。
而DH具体区别在下图:
为了防止在DH参数交换时,数据过大,DH使用的是取模数的方式,这样就能限制传输的值永远在[1,p-1]。这里,先说明一下DH算法的基本条件:
基本流程就是:
我们只要把上图的DHparameter替换为相对应的X/Y即可。而最后的Z就是我们想要的Premastersecret。之后,就和RSA加密算法一致,加上两边的random-num生成sessionKey。通过,我们常常称DH也叫作EphemeralDiffie-Hellmanhandshake。因为,他每次一的sessionKey都是不同的。
而RSA和DH两者之间的具体的区别就在于:RSA会将premastersecret显示的传输,这样有可能会造成私钥泄露引起的安全问题。而DH不会将premastersecret显示的传输。
上面内容大概讲清楚了基本的TLS/SSL的加密过程。不过,其中,还有很多其他的小细节,比如SNI,ALPN,ForwardSecrey。接下来,我们主要将这些细节将一下,因为他们其实也很重要。
FS(ForwardSecrey)主要是针对privatekey进行描述了。如果你的privatekey能够用来破解以前通信的session内容,比如,通过privatekey破解你的premastersecret,得到了sessionKey,就可以解密传输内容了。这种情况就是non-forward-secrey。那如何做到FS呢?很简单,上文也已经提到过了,使用DH加密方式即可。因为,最后生成的sessionKey和privatekey并没有直接关系,premastersecret是通过g(ab)modP得到的。
简单的说就是,如果你想要启用FS,那么你应该使用的是DH加密方式,而放弃RSA。不过,由于历史原因(TLS版本问题),RSA现在还算是主流的加密方式。但,DH也凭借他5S的安全性,份额也在增加。
ALPN全称是ApplicationLayerProtocolNegotiation(应用层协议协商机制)。看到应用层,程序员们应该都能反应出OSI7层网络协议。在应用层中,HTTP协议应该是重点。不过,由于HTTP版本的问题,以及现在HTTP2的流行,为了让client-server使用相同的协议而出现了ALPN。ALPN实际上是从SPDY中的NPN协议衍生出来的。不过,它们俩的机制正好相反。
总的来说,NPN已经退出历史的舞台了。。。ALPN现在是IETF指定的标准协议。ALPN在TLS具体的过程是:
SNI的全称为:ServerNameIndication。该机制的提出的意义是,当有一个server同时处理了很多个host时。相当于,一个IP映射多个域名,但,由于证书只能对一个3级域名有效,所以,针对于多个host来说,server为了能同时兼顾这些域名。一种简单的办法就是重定向到指定域名,如果都想支持的话,也行,掏钱自己多买几个证书(真土豪)。如果,你很土豪的话,现在就有这样的情况,一个IP服务器下,搭载了支持多个域名的server,并且每个域名都有合法的CA证书。那么,server怎么判断,哪一个域名用哪一个证书呢?这时候,就需要用到SNI。相当于在TLS阶段,将host一并发送过去,然后server就知道在serverhello阶段该返回啥证书了。现在,有个问题,为什么一定要用SNI呢?我们回想一下,这里我们仅仅只是建立TCP+TLS连接,客户端的一些内容,比如hostname,我们并不能在TCP中获得。而,想要获得的话,就需要等到HTTP阶段,获得client传过来的host或origin字段。所以,为了解决这个比较尴尬的点,就提出了SNI。
SessionID是server将上一次成功连接的session内容存在自己的硬盘里面。这里,就不涉及对sessiondata的二次加密。基本的过程是:
那么相对于完全的TLS/SSL连接来说,这里只用到了一次RTT。那么协议过程就变为了:
前面大致说了TLS/SSL是怎样运作的,以及有哪些连接方法。相当于,学画一条线一样,我们现在只知道这条线该画多长,但还不知道,这条线从哪里画。所以,接下来,我们就需要来探讨一下,两端发生了什么。其实也不难,主要还是关于CA证书的存放和验证。server端的很简单,就是把自己的CA证书发过来就ok。但,client验证这个证书是否可信,会有点复杂。首先,证书颁发机构就那么一些,换句话理解就是,每个证书颁发机构,就代表着一张CA证书。但,现在市面上的HTTPS网站,辣么辣么多,难道他们都用同一张证书?难道他们都有一样的pu/prkey那么HTTPS安全还有用吗?所以,按照上面的推理,我们的网站上的HTTPS证书,肯定都是各不一样的。一般来说,有3种类型的证书:DV(DomainValidation),OV(OrganizationValidation),EV(ExtendedValidation)。均价按照顺序上升,所以,最便宜的就是DV,这应该是我们勤劳的贫苦大众用得起的。它们之间具体的区别在于域名的支持上:
那我们的证书在芸芸证书中,是处于哪一个层级呢?一般是三级。怎么体现的呢?
那这么多证书,我们用的是哪一个呢?当然是,最下面那个。因为每个证书并不是都被信任,所以客户端首先就要了解一下,你这个证书能否用来进行验证。如果不行的话,那么你这次连接就是不被信任的,就没有绿色的小锁。这就需要了解一下,客户端的验证过程。
如果上述任一步骤出现问题,那么该次TLS/SSL就不会进行,会回退。那么它们在询问的时候,会不会发送网络请求呢?不会~因为,电脑在初始化时,会自带很多可信任的证书机构(即,RootCA),也就是我们刚刚提到的VeriSignClass3的证书机构。以及,能够签发证书的二级机构(比较少)。到时候,浏览器会自动的根据数字签名来进行证书的验证。
上面已经阐述了,CA证书的合法性是自下而上的验证方式。那么它们具体验证协议是怎样的呢?在说之前,我们先说几个概念:
CA验证首先需要说一下它的颁发过程:
然后,验证过程就是根据这个来的:
另外,为了证书的可靠性,提出了CertificateTransparency项目,实际上,就是让证书机构公开它的签发流水。防止出现重复签发。
CRL(CertificateRevocationList),即,证书吊销列表。CA机构会生成一个列表,列表里面是当前周期被吊销证书的序列号,当进行证书验证时,同样也会进行验证该项。如果,已经是吊销证书的话,那么该次TLS/SSL连接也会失败。我们可以从证书信息中找到CRLURI:
该协议虽然简单,但,缺陷还是比较多的。
OCSP(OnlineCertificateStatusProtocol),即,在线证书状态协议。它通过在线请求的方式来进行验证,不需要下载整个list,只需要将该证书的序列号发送给CA进行验证。当然,验证通过也会有一定的缓存期。不过,由于验证也会存在时延。另外,部署OCSP对CA也有一定的要求,CA要搭建的一个服务器来接受验证,并且,该服务器的性能要好(负载很大)。
session缓存设置可以让两次的RTT,变为一次,这相当于快了一倍(不包括,密钥计算等)。不同的server设置session的办法有很多,这里以nginx为例。在nginx中,支持的是SessionID的形式,即在server中缓存以前session的加密内容。涉及的字段有两个,ssl_session_cache和ssl_session_timeout。
看个demo吧:
ssl_prefer_server_cipherson;ssl_ciphersxxx;支持性最高的就是使用:
ssl_ciphersECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5不过,以下的加密套件,最好不要使用,因为基本上都不安全:
上面那些只能给一些远古浏览器使用,基本上在选择中是作为垫底的选择。
另外,怎么在nginx中开启FalseStart呢?这其实和服务器并没有多大的关系,关键还是你选择的套件和NPN/ALPN协议的作用。
那么,在nginx中,我们只要选择好合适的加密套件即可。这里就放一份现成的吧
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';使用DH密钥交换DH的加密过程,上面已经说过了。DH自带两个公共的参数,所以,这必须手动进行创建(实际上就是将参数sign一遍)。
//创建一个DHparamopenssldhparam2048-outdhparam.pem然后,调用该文件
ssl_dhparamdhparam.pem;这样,你就正式的开启的DH加密模式。如果你使用抓包工具观察一下,此时DH应该会在ServerHello里:
不过,由于历史原因,DHparam已经使用的长度是1024,比如:采用Oakleygroup2版本。现在,比较流行的DH加密方式是ECDHE,它和以前的加密方式(DHE)比起来,在密钥生成这块会快很多。同样,由于历史原因,它的基本条件比较高:(其实也还好)
catintermediate/certs/intermediate.cert.pem\certs/ca.cert.pem>intermediate/certs/ca-chain.cert.pem;那么ca-chain.cert.pem就是OSCPstapling验证文件。然后在nginx开启即可。
ssl_staplingon;ssl_stapling_verifyon;ssl_trusted_certificateca-chain.cert.pem;resolver8.8.8.88.8.4.4;//默认使用Google的关于DNS解析,同样你也需要问一下证书提供商,当然,该值可以不用管。下面也同样适用
ssl_staplingon;ssl_stapling_verifyon;ssl_trusted_certificateca-chain.cert.pem;开启过后,你可以使用openssls_client-connectwww.yourDomainName.com:443来测试一下,检测是否开启成功。
SNI就是针对一个IP手握很多张证书时,用到的协议机制,这主要是用来区分,不同的host,使用不同的证书。SNI详情上面已经说过了,这里就不赘述了。主要使用格式就是不同的server_name搭配不同的certificate
server{server_namewww.abc.com;ssl_certificateabc.crt;ssl_certificate_keyabc.crt.key;}server{server_namewww.def.com;ssl_certificatedef.crt;ssl_certificate_keydef.crt.key;}如何开启呢?换个高版本的nginx就行了。你可以使用nginx-V检查你的nginx是否带有