前言
关于HTTPS
,基本上你想知道的都在这里了(当然仅限入门)。本文原标题《HTTPS原理与实践》,下图是本文配套PPT的目录截图:
原理篇
2.1. 认识HTTPS
先说一下,本文可能有些地方由于描述不到位或者我本人理解错误而出现不准确内容,有错误欢迎指正!
2.1.1. 什么是HTTPS
HTTPS
全称Hyper Text Transfer Protocol over Secure Socket Layer
,直译过来就是通过SSL实现的超文本传输协议,简单来讲就是加密版的HTTP协议,也就是HTTP+SSL/TLS
。
为什么需要加密版的HTTP呢,因为我们都知道,HTTP是明文传输的,因此使用HTTP协议传输隐私信息非常不安全,很容易在传输过程中被窃取,或者通信内容被人篡改冒充,使用HTTPS可以避免这些问题。
2.1.2. SSL/TLS
为了解决HTTP明文传输的风险性,网景公司设计了SSL(Secure Sockets Layer
)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。SSL
目前的版本是3.0,被IETF(Internet Engineering Task Force
)定义在RFC 6101
中,之后IETF对SSL 3.0进行了升级,于是出现了TLS(Transport Layer Security
) 1.0,定义在RFC 2246。实际上我们现在的HTTPS都是用的TLS协议,但是由于SSL出现的时间比较早,并且依旧被现在浏览器所支持,因此SSL依然是HTTPS的代名词。
上面一大段话是转载的,简单而言就是TLS是SSL的升级版,现在浏览器一般用的都是TLS。
2.1.3. HTTPS的优点
从底层分析HTTPS有以下3大优点:
- 防窃听:信息被加密了,第三方拿不到密码,所以可以防窃听;
- 防篡改:还是因为信息被加密了,如果执意篡改的话,客户端无法解密不说,而且数据完整性校验这一关也过不了;
- 防冒充:防止攻击者冒充他人身份参与通信,比如HTTPS的双向认证;
从实际出发来看,HTTPS主要有以下优点:
- 防止私密信息泄露,防止信息被篡改;
- 有助于SEO,百度、谷歌均明确表示会优先收录、展示HTTPS站点的内容;
- 完全杜绝运营商HTTP劫持问题;
- 有效解决运营商DNS劫持问题,降低网站被劫持的风险;
HTTPS
的小绿锁表示可以提升用户对网站信任程度(当然不是说有小绿锁的都是安全的);- 可以有效防止山寨、镜像网站等;
- 为未来升级
HTTP/2
做准备,HTTP/2
必须基于HTTPS
部署;
下面单独对3和4说一下。
运营商HTTP劫持我们都知道,经常莫名奇妙的在自己网站上看到各种恶意的恶心广告,查看代码发现被注入了一些奇奇怪怪的js,这其实就是HTTP劫持,启用了HTTPS之后,我们传输的内容都是加密的,运营商想篡改内容都没辙。
为什么HTTPS能够有效的解决DNS劫持呢?在域名被劫持的情况下,客户端访问的实际上是攻击者的IP,客户端通过这个IP找到攻击者的服务器,要求建立HTTPS通信,因为攻击者没有真实服务器的证书和私钥(想要申请这个域名的证书必须验证自己是这个域名的所有者,攻击者做不到这一点),把伪造或自签名的证书提供给客户端,是得不到浏览器的认可的,浏览器会弹出不安全的警告。但是用户执意要访问我们也没办法,所以才用了有效解决而不是彻底解决这一词。
另外,就近看的话,越来越多的场合强制要求https(比如ios、微信小程序等等都要求https),从长远看,升级HTTPS是大势所趋,一次升级,永久受益。
2.1.4. HTTPS的缺点
- 服务器性能下降,开启
HTTPS
会增加内存、CPU、网络带宽的开销,特别是非对称加密这一块; - 访问速度下降,HTTP连接的建立需要3次握手,HTTPS还需要加上ssl的几次握手(具体是几次没去考究,有说是9次),当然下降主要是在第一次建立连接的时候,后面正常通信速度一般没啥变化;
- 除了握手部分外,所有信息传输之后浏览器和服务器都要进行加密解密,又是一笔额外的开销;
- 申请证书需要一笔花费,当然现在免费证书也很容易申请到,这个不算明显缺点;
一句话概括就是:性能和速度下降,但是具体下降多少呢,这个不太好讲,可以延伸阅读:HTTPS 要比 HTTP 多用多少服务器资源?
2.1.5. HTTPS原理
HTTPS主要分为单向认证和双向认证,99%的场景都只需要单向认证,双向认证我们后面再来讲。
2.1.5.1. 小明和小红的故事
在介绍HTTPS原理之前,我们先来假设,假如让我们自己设计一套加密方案该如何实现呢?为了更好理解,我们用人来举个栗子:
小明(客户端)暗恋小红(服务端),想给小红写信,但是又怕信在邮递的过程中被人偷看,所以他把信装在一个盒子里锁起来,信虽然是安全了,但是小红就算收到了打不开啊,把钥匙给小红寄过去?万一钥匙也被人偷去了怎么办?
就在这时,小红想到了一个办法:小红把一个没有上锁的盒子寄给小明(钥匙在小红这),小明把钥匙放进去再锁上(小明可以锁盒子,但是一旦锁上了就连自己都打不开了),然后把这个装有钥匙的盒子寄回给小红,因为小红有钥匙,所以能打开盒子拿到里面的锁信件盒子的钥匙,再然后就能看到小明给她写的信了。同时,小红想回信的话,也可以把信装在这个盒子里寄回去,因为这是小明的盒子,他自己肯定还有一把锁,所以他也能打开,从此,小明和小红就可以任意的写信私通而不用担心其他人能看到了!
但是,如何保证小红寄送过来的未上锁盒子不被别人调包呢?这个等到我们介绍完HTTPS原理之后我们再回过头来看这个问题。
2.1.5.2. 单向认证
下面这张图是网上比较常见的一张图:
这个应该算是精简版,省略了一些细节,但是对于理解https的核心原理部分足够了。下面这张图可能更全面(图片来源于这里,感谢作者)
具体过程如下:
- 浏览器访问一个https地址,同时向服务端发送支持的SSL协议版本、支持的加密算法、一个客户端生成的随机数(用于稍后生成"对话密钥")等。
- 服务端给客户端返回要使用的SSL协议版本号、加密算法、一个服务器生成的随机数(也同样用于稍后生成"对话密钥"),另外还有一个最重要的,返回服务器端的证书,即公钥证书;
- 客户端收到服务器回应以后,首先验证证书是否合法,如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期、或者返回的公钥不能正确解开返回证书中的数字签名,就会向访问者显示一个警告,由其选择是否还要继续通信。如果证书没有问题,客户端就会从证书中取出服务器的公钥继续;
- 客户端向服务端发送自己所能支持的对称加密方案(注意是对称加密),供服务器端进行选择;
- 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式;
- 服务器将选择好的加密方案通过明文方式返回给客户端;
- 客户端接收到服务端返回的加密方式后,使用该加密方式生成产生一个随机密钥(后续用作通信过程中对称加密的密钥),然后将该随机数用用证书中的公钥加密发给服务端;
- 服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获得最终的对称加密密钥。至此,客户端和服务端都得到一个随机密钥,并且这个密钥别人没法知道;
- 在接下来的会话中,服务器和客户端将会使用该密码进行对称加密解密,保证通信过程中信息的安全。
至于为什么会有三个随机数来生成最终的对称加密密钥,主要是为了进一步增大密钥的随机性,这里有一段不错的解释:
不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。
需要特别注意的是,以上握手阶段全部用的是HTTP协议明文传输的。
2.1.5.3. 回到小明和小红的故事
介绍完HTTPS原理之后,我们再来回到小明和小红的故事来加深理解。看起来让小红寄未上锁盒子的方案很完美,但是,假如小红寄过来的盒子被人调包了呢?调包的话打开情书的钥匙就被别人拿走了。所以这时候需要有一家在江湖上比较有威望的镖局(假设叫龙门镖局)来帮忙押送小红的盒子,当小明收到盒子时,如果是镖局亲自送过来的、并且说是小红寄的盒子,那么小明就认为这是小红的盒子(小明很信任这家镖局),前面我们说了,这家镖局在江湖上很有威望,为了自己的声誉它必须保证每一趟镖都没问题。有人又会问了,干嘛不直接让镖局把信送过去呢?因为小明写信不可能只写一封,而且每封情书都让镖局来押送成本太大,而且镖局比邮局流程更繁琐,更费时间,如果小明收到一个没听过的镖局送过来的声称是小红寄的盒子,那么他不会相信。
上面例子中,小明是浏览器,小红是服务器,镖局是颁发证书的CA(后面会提到),没听过的镖局就是不受信任的根证书,镖就是证书,信被别人看了叫HTTP明文泄露,信被别人改了叫HTTP劫持,信上的地址被人改了叫DNS劫持,小明寄给小红的盒子是对称加密(因为双方都有钥匙,都能打开),小红寄给小明的没锁的盒子可以看做是非对称加密(因为所有人都可以拿它加密东西,但是一旦加密了就打不开,只有小红一个人有钥匙),镖局自己干坏事自毁前程就等同于沃通的故事,等等。
2.1.5.4. 双向认证
双向认证和单向认证原理基本差不多,主要区别是除了客户端需要认证服务端以外,服务端对客户端也需要认证。什么场景下需要验证客户端呢?比如一些银行业务,银行会要求客户必须在电脑上插入它们签发的U盾之类的东西,或者安装什么控件,这里就类似客户端证书的概念,没有这个证书的人无法使用银行提供的业务。
双向认证我没有去亲自尝试,可以参考这篇文章。
下面还是上面那位网友画的双向认证图,我没有仔细考究,先贴在这里:
2.1.6. 引申思考
2.1.6.1. 如何保证公钥不被篡改
将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
那如何保证证书可信呢?证书的生成都是由国际顶级认证机构签发的,签发的时候必须验证你是不是这个域名的所有者,只有是才给你签发证书(证书和域名一一挂钩),所以理论上其他人无法伪造你的证书(只是理论上),即使伪造了,浏览器在加载的时候会进行域名校验,如果证书中的域名和实际域名不匹配,浏览器是会警告的。
问题又来了,浏览器怎么知道你的证书是不是合法的国际顶级认证机构签发的呢?操作系统和浏览器都内置了一些他们认为可信任的国际顶级认证机构(CA,后面会提到),只有他们签发的证书浏览器才信任。
2.1.6.2. 为何正式传输时使用对称加密
因为非对称加密很慢,而且传输普通内容时双方都已经拿到了随机又没有其他人知道的密码,所以普通的对称加密足矣。
2.2. CA
2.2.1. 何为CA
CA
(Certificate Authority
),即证书认证机构,它的作用就是提供证书(即服务器证书,由域名、公司信息、序列号和签名信息组成)加强服务端和客户端之间信息交互的安全性,以及证书运维相关服务。任何个体/组织都可以扮演 CA 的角色,难的是你要能得到全世界各大操作系统、浏览器等的默认信任,能得到他们的默认信任,你也可以自己开一家CA公司了,这类证书通常叫做根证书。浏览器默认信任的 CA 大厂商有很多,比如 Symantec
赛门铁克、Comodo
、Godaddy
、GolbalSign
(百度微博等都是它签发的) 和 Digicert
。
如何查看操作系统内置了哪些CA呢?Internet
选项->内容->证书->受信任的根证书颁发机构:
想让你的https网站默认情况下就被全世界浏览器信任你必须使用它们签发的证书,大部分都是要钱的,当然也有免费的。
很多人一开始应该有和我一样的疑问,我升级我的https,凭啥要到别人那里花钱买证书(即使有免费的也不爽)?,看到这里我想不用再多解释你应该懂了。
2.2.2. CA如果作恶怎么办
CA必须是绝对公平公正不作恶的,否则一旦暴露出来一些丑闻,立即会被各大浏览器和厂商拉入黑名单不再信任,或者各种降级处理,带来的后果甚至可能是杀身之祸,比如被360收购的沃通的下场:谷歌宣布开始全面封杀使用沃通CA证书网站,信誉破产的恶果。
沃通的主要罪状有五个:
- 秘密收购自己的根CA公司startcom;
- 只要有子域名的权限就可以得到顶级域名的权限,这个很危险,比如github是开放子域名给任何用户的;
- 伪造证书签署日期;
- 签署多个相同序列号证书;
- 邮件恐吓使用其它免费证书的用户说他们的证书不安全;
沃通为何要伪造签署日期呢?
由于如今的运算能力越来越强,SHA1 散列算法的可靠性越来越不够了。一些主流的浏览器,如果发现2016元旦之后签署的 CA 证书,依然采用 SHA1,会给出警告。沃通为了帮证书申请人规避浏览器警告,故意把签署日期伪造成2015年底。
2.2.3. 顶级证书私钥泄露怎么办
如果顶级证书的私钥泄露,那么带来的后果是灾难性的,只能各大浏览器和操作系统通过更新补丁的方式来吊销泄密的根证书,泄露之后一般这家CA公司离倒闭不远了,也不是没有这样的例子:
荷兰CA供应商DigiNotar因为黑客入侵事件而成为万众瞩目的焦点,它发行的证书被众多浏览器开发商和操作系统开发商宣布为不受信任。现在,这家失去信任的CA宣告破产。疑似伊朗黑客在7月中旬入侵了DigiNotar服务器,发行了531个伪造证书,包括了Google、微软、雅虎、Twitter、Facebook、中情局、军情六处和摩萨德等。DigiNotar在7月19日发现了入侵,但直到8月份外界才知道入侵事件。DigiNotar的母公司VASCO承认损失惨重。
2.3. SSL证书
2.3.1. 证书的种类
SSL 证书按大类一般可分为DV SSL
、OV SSL
、EV SSL
证书,又叫域名型、企业型、增强型证书:
- DV SSL(
Domain Validation
),域名型SSL证书,证书颁布机构只对域名的所有者进行在线检查,只要你能证明你是这个域名的所有者就可以给你颁发证书,不会校验网站的资质,哪怕你是个黄赌毒网站也可以,个人网站、非盈利项目、开源项目等用域名型证书足矣; - OV SSL(
Organization Validation
),企业型SSL证书,需要购买者提交组织机构资料和单位授权信等在官方注册的凭证,证书颁发机构在签发 SSL 证书前不仅仅要检验域名所有权,还必须对这些资料的真实合法性进行多方查验,只有通过验证的才能颁发 SSL 证书; - EV SSL(Extended Validation),增强型SSL证书(EV SSL),验证流程更加具体详细,验证步骤更多,这样一来证书所绑定的网站就更加的可靠、可信。它跟普通
SSL
证书的区别是浏览器的地址栏变绿,如果是不受信的 SSL 证书则拒绝显示,如果是钓鱼网站,地址栏则会变成红色,以警示用户。
不论是 DV、OV 还是 EV 证书,其加密效果都是一样的,个人网站用DV证书、企业用OV足够了。
如何查看证书类型?如下图,百度是OV,github
是EV(其实好像并没有统一的区分方法,有的证书不会写):
2.3.2. 证书的格式
一般来说,主流的 Web 服务软件,通常都基于 OpenSSL 和 Java 两种基础密码库。
Tomcat
、Weblogic
、JBoss
等Web
服务软件,一般使用JDK自带的Keytool
工具,生成Java Keystore
(JKS)格式的证书文件。Apache
、Nginx
等Web
服务软件,一般使用OpenSSL
工具提供的密码库,生成.key
、.crt
等格式的证书文件。- IBM 的 Web 服务产品,如 Websphere、IBM Http Server(IHS)等,一般使用 IBM 产品自带的 iKeyman 工具,生成 KDB 格式的证书文件。
- 微软的IIS使用
Windows
自带的证书库生成pfx
格式的证书文件。
详见下图:
每种格式之间都可以相互转换,转换方法可以参考阿里云给出的帮助文档,在腾讯云申请证书的时候会自动帮你生成4种服务器各自需要的格式,非常方便。
2.3.3. 证书的收费与免费
不是说收费的证书就一定比免费证书好,只不过现阶段各CA机构为了自己的盈利目的,免费证书一般都有一些限制,比如只支持单域名,子域名太多的话需要挨个申请。
实践篇
本文全部以nginx
配置为例,其它Web服务器配置请自行google,或者参考这篇不错的文章:SSL证书安装指引。
3.1. 第一步,生成证书
首先我们需要得到一张证书,可以申请免费的,也可以自制,本地测试时一般自制证书。
3.1.1. 申请免费证书
免费证书一般有下面几种:
Let's Encrypt
:获得Mozilla、微软等主要浏览器厂商的根授信,出现的目的就是为了推广HTTPS,缺点是只有3个月有效期,过期需续签;TrustAsia
亚洲诚信;StartSSL
免费DV证书:因丑闻被主流浏览器封杀,一般不建议;Wosign
(沃通)免费DV证书:同上,被各大浏览器封杀,基本可以无视了;
我们一般不需要自己直接和它们打交道,国内的云服务提供商一般都有免费的证书申请,这里操作会更简单一点。
先声明一下,这里不是打广告,如果有更好用的,欢迎大家推荐。阿里云之前有一款免费的SSL证书申请,但是现在好像下线了,腾讯云有一款TrustAsia
的免费证书,又拍云说是有2款免费证书,而且可以自动续签,但是试了一下发现必须把域名绑定到它们的CNAME
才行,所以想想还是放弃了:
最后我用的是腾讯云免费SSL证书,有一款TrustAsia
的免费证书,缺点是有效期只有1年(到期应该可以重新申请),且不支持泛域名,如果子域名很多的话比如挨个申请,很麻烦,大家有更好用的欢迎推荐:
申请很简单,简单填写资料,然后验证域名身份,快的话2-3分钟内就能成功申请到:
成功后可以把证书下载到本地,内置了Apache
、IIS
、Nginx
、Tomcat
的4种证书格式,很方便。
申请到的证书:
3.1.2. 自制证书
虽然有很多途径可以申请到免费的证书,但一般都是单域名(不支持泛域名),限制太多,本地调试极不方便,所以我们希望本地开发时能够使用我们自己签发的证书。下面我们就来自己实践一下如何从头开始自制HTTPS证书。
证书一般都是用openssl
来生成,当然也可以用jdk自带的keytool
来生成,但是最终还是要用openssl
来转换格式,所以一般还是推荐用openssl
来生成。
3.1.2.1. 安装openssl
首先当然还是安装openssl
,这里我们只介绍Windows平台,Linux系统的请参考这里,点击这里下载Win64位的安装包:
安装很简单,安装完毕之后为了使用方便,建议配置一下环境变量:
然后就可以随时随地执行openssl
命令了。
3.1.2.2. 生成CA根证书
我们先生成一个自己的CA根证书ca.crt
,然后再用这个根证书生成服务端证书server.crt
,有人会问,为啥不直接生成服务端证书呢?因为这样做的话将来我们只要导入一个CA根证书,其它所有用它生成的证书都默认是可信任的,方便嘛!
好了,说了这么多废话,下面开始了:
# 生成CA私钥
openssl genrsa -out ca.key 1024
# 生成CA根证书,-day指定证书有效期
openssl req -new -x509 -key ca.key -out ca.crt -days 365
就这么简单,第二步需要输入几个东西,虽然说乱填也可以,但建议按照提示来填,其中Common Name
需要特别注意,如果是生成CA证书的话,可以输入诸如My CA
之类的,如果是生成服务端证书的话,必须输入网站的域名,可以输入泛域名,如*.haoji.me
:
另外,如果报了unable to write 'random state'
的错误,一般都是因为没有使用管理员权限打开命令行窗口。
3.1.2.3. 生成服务端证书
和CA证书的生成不同的时,我们需要先生成一个csr
证书请求文件文件(CSR,Cerificate Signing Request
),有了这个文件之后再利用CA根证书生成最终的证书:
# 生成服务端私钥
openssl genrsa -out server.key 1024
# 生成证书请求文件
openssl req -new -key server.key -out server.csr
# 生成最终证书文件,-day指定证书有效期
openssl x509 -req -days 365 -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt
操作截图:
但!是!各位看官先别急着去实践,以上证书我在Chrome55下测试没问题,但是Chrome61下测试提示Subject Alternative Name missing
错误:
The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.
提示必须指定DNS Name
或者IP地址,而指定这个必须要用v3的证书。生成v3证书只需要比上面多加2个参数-extfile openssl.cnf -extensions v3_req
,但是多了一个额外的配置文件,这个文件里面可以填很多东西(完整配置文件参考这里),我们这里仅仅需要指定subjectAltName
即可。
先准备一个名为openssl.cnf的文件,内容如下:
[v3_req]
subjectAltName = @alt_names