概念
HTTP和HTTPS
HTTP协议:超文本传输协议(Hyper Text Transfer Protocol)。是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。 使用 HTTP 协议时,传输的数据是不安全的,因为所有在客户端和服务端往来的数据都是明文。
- 存在三大风险:
- 窃听风险(eavesdropping):第三方可以获知通信内容。
- 篡改风险(tampering):第三方可以修改通信内容。
- 冒充风险(pretending):第三方可以冒充他人身份参与通信。 为了解决这个问题,需要在HTTP传输协议中加入一种安全机制(SSL/TLS),从而引出了HTTPS协议。 HTTPS可以理解为HTTP和SSL/TLS的结合。其中SSL/TLS的引入解决了安全问题,而上层应用协议还是HTTP。
SSL/TLS
SSL(Secure Sockets Layer)中文称作“安全套接层”,TLS(Transport Layer Security),中文称作“传输层安全协议”。
- 为了解决上文提到的三大风险:
- 所有信息都是加密传播,第三方无法窃听。
- 具有校验机制,一旦被篡改,通信双方会立刻发现。
- 配备身份证书,防止身份被冒充。
历史
1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。 1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。 1996年,SSL 3.0版问世,得到大规模应用。 1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。 2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版。
目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流客户端都已经实现了TLS 1.2的支持。 TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。
对称加密(Symmetric Cryptography)
对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。对称加密有很多种算法,由于它效率很高,所以被广泛使用在很多加密协议的核心当中。
对称加密通常使用的是相对较小的密钥,一般小于256 bit。因为密钥越大,加密越强,但加密与解密的过程越慢。如果你只用1 bit来做这个密钥,那黑客们可以先试着用0来解密,不行的话就再用1解;但如果你的密钥有1 MB大,黑客们可能永远也无法破解,但加密和解密的过程要花费很长的时间。密钥的大小既要照顾到安全性,也要照顾到效率。
常见的对称加密算法:DES,AES,3DES等等。
非对称加密(Asymmetric Cryptography)
非对称加密为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人--银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。
常见的非对称加密算法:RSA,ECC,DH,ECDHE,ECDH
数字证书认证机构
数字证书认证机构(Certificate Authority,缩写为CA),是负责发放和管理数字证书的权威机构,并作为受信任的第三方,承担公钥体系中 公钥合法性检验 的责任。
CA 中心为每个使用公开密钥的用户发放一个数字证书,数字证书的作用是证明证书中列出的用户合法拥有证书中列出的公开密钥。
全球权威的 CA 中心的证书已被各软件厂商设置为 可信任的根证书。所谓 根证书 是指此证书是受信任的起始点,可以使用此证书来证明其它证书。这些证书被预置到软件内的过程是未知的,这也是最关键的安全环节。
数字证书
数字证书是由CA机构颁发的证明(也就是问题中提及的CA证书),它包含了公钥、公钥拥有者名称、CA的数字签名、有效期、授权中心名称、证书序列号等信息。我们可以通俗的理解为数字证书是个人或企业在网络上的身份证。
在使用数字证书的过程中应用加密技术,能够实现:
- 身份认证: 在网络中传递信息的双方互相不能见面,利用数字证书可确认双方身份,而不是他人冒充的。
- 保密性: 通过使用数字证书对信息加密,只有接收方才能阅读加密的信息,从而保证信息不会被他人窃取。
- 完整性: 利用数字证书可以校验传送的信息在传递的过程中是否被篡改过或丢失。
- 防抵赖: 利用数字证书进行数字签名,可准确标示签名人身份及验证签名内容,因此签名人对签名及签名内容具有不可否认性,其作用与手写签名具有同样的法律效力。
数字签名
数字签名,又称公钥数字签名、电子签章,是使用公钥加密技术实现的用于鉴别数字信息的方法。一套数字签名通常定义两个互补的运算,一个用于生成签名,另一个用于验证签名。
签名:
- CA机构拥有自己的非对称加密的公钥和私钥。
- CA机构对证书原文O(申请者的公钥、身份信息和证书有效期等信息)通过hash算法进行hash得到散列值H。
- CA机构使用自己的私钥对散列值H进行加密得到数字签名S。 证书原文O和数字签名S共同组成了数字证书,这样一份数字证书就可以颁发给网站了。
验证:
- 客户端或者应用软件(简称接收方)。接收方收到数字证书后,得到证书原文O和数字签名S。
- 接收方通过CA机构的公钥对数字签名S进行解密得到散列值S'。
- 接收方用证书里指明的hash算法对证书原文O进行hash得到O'。
- 通过 S' 和 O' 的对比,如果相同,代表证书内容的真实、完整、可靠;如果不相同,则说明消息在传输过程中被篡改过。
HTTPS的加密机制
对于HTTP网络传输为何要加密?
HTTP协议中的报文都是以明文的方式进行传输,明文数据会经过中间代理服务器、路由器、WiFi热点、通讯服务代理商等多个物理节点,如果数据在传输过程中被劫持,数据内容就会完全暴露了。劫持者就可以在不被对方察觉的情况下窃听、篡改、冒充这些明文数据内容。从而导致数据传输的不安全,所以我们需要对这些传输的数据进行加密。那到底是用对称加密还是非对称加密呢?
用对称加密?
假如通讯过程双方共同持有同一个密钥,而这个密钥约定好的,双方的通信通过该密钥加密后传输可以说是相对安全的(为啥说相对呢,固定的密钥在很多次的传输中很容易被掌握规律,从而密钥被破解)。
但是密钥是要传输的,如何能做到在传输过程中只有客户端和服务端双方知道,而不被中间人知道?如果服务端创建一个密钥并传输给客户端,如何做到该密钥不被别人劫持?假如别人劫持到该密钥,就可以通过密钥解出服务端和客户端传输的内容,这样和HTTP明文传输没有啥区别。
假如,客户端在请求服务端的时候就已经预测了双方约定好的密钥,这种情况确实能防止密钥因为要通过传输被劫持的发生,可客户端就得预存好全世界所有网站的密钥。这是一种不现实的做法,因此对称加密没办法满足信息安全,那使用非对称加密呢?
用非对称加密?
非对称加密是是有一把公钥,一把私钥。根据公钥加密内容只有私钥能解开,私钥加密内容只有公钥能解开的原则。
(一)使用单向非对称加密的方式。服务端拥有一把私钥,然后公钥是公开的,客户端拿到公钥,就能解开服务端的加密信息获取内容,而客户端加密内容,只有服务端私钥才能解开,这样好像传到服务端的加密内容就有了安全的保障。 但是:
- 公钥是公开的,说明除了客户端外,中间人也同样会得到公钥,服务端用私钥加密的内容,中间人同样可以用公钥破解加密内容。
- 中间人可以伪造自己的私钥和公钥并把自己的公钥传给客户端,通过置换公钥,实现通过自己的私钥解密得到客户端发给服务端的内容再用服务端公开的公钥加密想发送的内容给服务端,从而达到窃听、篡改、冒充的目的。
(二)使用双向非对称加密的方式。客户端和服务端均拥有一个公有密匙和一个私有密匙。公有密匙可以对外暴露,而私有密匙只有自己可见。使用公有密匙加密的消息,只有对应的私有密匙才能解开。反过来,使用私有密匙加密的消息,只有公有密匙才能解开。这样客户端在发送消息前,先用服务器的公匙对消息进行加密,服务器收到后再用自己的私匙进行解密。 中间人都没办法拿到相应的私钥要解密内容,无法得到双方的对话信息,好像这种加密满足安全性问题。但是:
- 非对称加密算法非常耗时。
- 中间人可以在客户端与服务端交换公匙的时候,将客户端的公匙替换成自己的。这样服务端拿到的公匙将不是客户端的,而是服务端的。服务端也无法判断公匙来源的正确性。
- 中间人可以不替换公匙,但是他可以截获客户端发来的消息,然后篡改,然后用服务端的公匙加密再发往服务端,服务端将收到错误的消息。
既然非对称加密都不能保障信息安全而且耗时,那如果是非对称加密和对称加密结合呢?
用非对称加密 + 对称加密?
服务端拥有非对称加密的私钥和公钥。客户端请求服务端时,服务端将公钥发给客户端。客户端拿到公钥,并随机生成对称加密的密钥X用公钥加密发给服务端。服务器拿到客户端发过来的加密内容用自己的私钥解密获取到客户端发过来的密钥X。双方都拿到了密钥X,这个在中间人无法得知的情况下,通过密钥X加密信息来进行对话。
这种方式似乎比较完美,也是HTTPS加密的方案。但是在双方都不会发现异常的情况下,中间人通过一套“狸猫换太子”的操作,掉包了服务器传来的公钥,进而得到了密钥X:
- 浏览器向网站服务器请求,服务器把公钥A明文给传输浏览器。
- 中间人劫持到公钥A,保存下来,把数据包中的公钥A替换成自己伪造的公钥B。
- 浏览器生成一个用于对称加密的密钥X,用公钥B(浏览器无法得知公钥被替换了)加密后传给服务器。
- 中间人劫持后用私钥B’解密得到密钥X,再用公钥A加密后传给服务器。
- 服务器拿到后用私钥A’解密得到密钥X。
主要原因是浏览端无法保证接收到公钥是不是来自服务端的。
那如何保证浏览端接收到的公钥是来自服务端?
此时我们应该请出CA机构,作为负责发放和管理数字证书的权威机构,并作为受信任的第三方。
网站在使用HTTPS前,需要向CA机构申领一份数字证书,数字证书里含有证书持有者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,证书就如身份证,证明“该公钥对应该网站”。
而为了 防止数字证书被篡改,数字证书包含数字签名和证书明文,通过比对证书内容和签名是否一致就能判别是否被篡改。
中间人有可能篡改该证书吗?
假设中间人篡改了证书的原文,由于他没有CA机构的私钥,所以无法得到此时加密后签名,无法相应地篡改签名。浏览器收到该证书后会发现原文和签名解密后的值不一致,则说明证书已被篡改,证书不可信,从而终止向服务器传输信息,防止信息泄露给中间人。
既然不可能篡改,那整个证书被掉包呢?
中间人有可能把证书掉包吗?
假设有另一个网站B也拿到了CA机构认证的证书,它想劫持网站A的信息。于是它成为中间人拦截到了A传给浏览器的证书,然后替换成自己的证书,传给浏览器,之后浏览器就会错误地拿到B的证书里的公钥了,这确实会导致上文“中间人攻击”那里提到的漏洞?
其实这并不会发生,因为证书里包含了网站A的信息,包括域名,浏览器把证书里的域名与自己请求的域名比对一下就知道有没有被掉包了。由此引出了HTTPS的加密(即SSL/TLS 协议)
HTTPS证书认证流程
- 客户端发起握手,告诉服务器客户端支持的SSL/TLS版本、数据加密算法、以及一个随机数ClientRnd
- 服务端确认支持这个版本的SSL/TLS、加密算法,并生成一个随机数ServerRnd
- 服务端将证书(包含服务端的公钥)和ServerRnd返回给客户端
- 客户端检测内置的"受信任的根证书颁发机构",查看解开数字证书的公钥是否在,如果解开数字证书的CA机构的公钥存在,则使用它解开数字证书,得到正确的服务器公钥,并生成第三个随机数PreMasterSecret
- 客户端使用数字证书带的公钥(服务端的公钥)将PreMasterSecret进行加密的值和ClientHash值(通过之前交换的随机数ClientRnd和ServerRnd进行Hash生成),发送给服务端,请求变更编码
- 服务端生成一个ServerHash值(通过之前交换的随机数ClientRnd和ServerRnd进行Hash)来校验ClientHash(确认不是假的客户端),并使用自己的私钥解密得到PreMasterSecret
- 服务端根据前面的随机数ClientRnd、ServerRnd和PreMasterSecret以及之前约定的加密算法,生成用于加密后续传输数据的会话密钥 SessionSecret
4.服务端将之前生成的ServerHash值,发送给客户端,确认开始变更编码
- 客户端使用之前生成的ClientHash值校验ServerHash (确认不是假的服务端),并以服务端生成会话密钥的方式生成会话密钥 SessionSecret
5.客户端使用会话密钥加密数据,并发送给服务端
- 服务端使用会话密钥解密数据,执行业务逻辑后产生数据
6.服务端使用会话密钥加密数据,返回给客户端
- 客户端使用会话密钥解密数据,完成一次和服务端的数据交换
上述的流程的几个注意点:
- 前面两次的随机数(ClientRnd和ServerRnd)都是以明文的形式进行传输的,其作用除了生成Hash验证客户端和服务端的真假外,还参与和第三个随机数(PreMasterSecret)共同算出会话密钥
- 上面提及非对称密钥是服务端的非对称密钥(其中公钥是携带在数字证书里面传给客户端的,私钥在服务端),参与对第三个随机数的加解密
- 数字证书可以找权威的证书授权中心颁发证书(有付费的,也有免费的),也可以自己做CA,然后自己给自己颁发证书,具体流程可查看相应的文档
在握手阶段都不加密(也没法加密),都是明文的,是否安全取决于PreMasterSecret是否被破解。 通过RSA算法加密是比较安全的,但还是有被破解的可能。理论上只要服务器的公钥足够长(比如2048位),那么Premaster secret可以保证不被破解。但是为了足够的安全,可以考虑把握手阶段的默认的RSA算法,改为 Diffie-Hellman算法(简称DH算法)。使用了DH算法后,Premaster secret不需要传递,双方只要交换各自的参数,就可以算出这个随机数(即由传递Premaster secret变成了传递DH算法所需的参数,然后双方各自算出Premaster secret)。
HTTPS必须在每次请求中都要先在SSL层进行握手传递秘钥吗?
握手阶段用来建立SSL/TLS连接。如果出于某种原因,对话中断,就需要重新握手。如果每次请求都经历一次密钥传输这个过程会非常耗时。那有没有啥办法来处理这个问题呢?
服务器会为每个客户端维护一个session ID, 在SSL/TLS握手阶段传给客户端。当客户端重新连接https 服务器时,SSL/TLS握手阶段,出示自己的session ID,服务端获得session ID,以此为索引,可以获得和该客户端共同拥有的SessionSecret(对话密钥),双方就可以重新使用已有的"对话密钥",而不必重新生成一把。
session ID是目前所有浏览器都支持的方法,但是它的缺点在于session ID往往只保留在一台服务器上。如果客户端的请求发到另一台服务器,就无法恢复对话。session ticket就是为了解决这个问题而诞生的,客户端不再发送session ID,而是发送一个服务器在上一次对话中发送过来的session ticket。这个session ticket是加密的,只有服务器才能解密,其中包括本次对话的主要信息,比如对话密钥和加密方法。当服务器收到session ticket以后,解密后就不必重新生成对话密钥了。目前只有Firefox和Chrome浏览器支持。