GoLang 专题:HTTPS 安全通信深度解析

发表时间: 2020-02-26 17:13

本文来源:腾讯优图实验室

笔者来自腾讯优图实验室,优图实验室专注于图像处理、模式识别、深度学习,在人脸识别、图像识别、医疗AI、交通、OCR 等领域积累了领先的技术水平和完整的解决方案。近年来,优图在计算机视觉领域的研究和应用积累了深厚的底蕴和丰富的行业落地经验。笔者所在的团队主要负责视觉AI能力赋能边缘计算设备的研发工作。一直以来,在实验室甚至整个公司特别强调“安全和隐私”,近半年,笔者在用 GoLang 开发智能边缘计算设备的网络通信项目时,常被要求务必重视“通信的安全和隐私”。期间,对接了多个合作方,有的要求“公网下要防止域名劫持”,有的要求“客户端上报要带‘证书’啊,更安全”,还有要求除了要用 HTTPS,还要在业务逻辑上再进行二次哈希、摘要、加密等等。由于笔者对 HTTPS 的认知也仅停留在 “HTTPS 比 HTTP 更安全”的层面上,在项目开发中遇到很多通信相关的问题经常束手无策,因此沉下心来,认认真真学习了 HTTPS 并记录成此文。


I. HTTPS 之灵魂三问

要说 HTTPS,搞 IT 的甚至是不搞 IT 的,都知道:“HTTPS 比 HTTP 安全”。因此但凡要开发涉及网络传输的项目时,得到的需求一定有:“要用 HTTPS”。



1.1 HTTPS 是什么?

维基百科对 HTTPS 的解释是:

超文本传输安全协议(英语:HyperText Transfer Protocol Secure,缩写:HTTPS;常称为 HTTP over TLS、HTTP over SSL 或 HTTP Secure)是一种通过计算机网络进行安全通信的传输协议。HTTPS 经由 HTTP 进行通信,但利用 SSL/TLS 来加密数据包。HTTPS 开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。这个协议由网景公司(Netscape)在 1994 年首次提出,随后扩展到互联网上。

抓重点:HTTPS=HTTP over SSL/TLS,也就是说,HTTPS 在传输层 TCP 和应用层 HTTP 之间,多走了一层 SSL/TLS。

由此可见,TLS/SSL 是 HTTPS 的核心! 那么,这个TLS/SSL又是何方神圣呢?文章《How to use SSL/TLS to Secure Your Communications: The Basics》 (
https://www.vircom.com/blog/how-to-use-ssltls-to-secure-your-communications-the-basics/) 指出:

The SSL/TLS protocol functions between two layers of the OSI Presentation layer.The handshake and record layers operate over TCP/IP to encrypt data received directly from the Application layer.

SSL/TLS 协议作用在传输层和应用层之间,对应用层数据进行加密传输。

借用文中的图,可以直观感受到:SSL和TLS都是加密协议SSL,全称 Secure Socket Layer,在 1994 年由网景公司(Netscape)最早提出 1.0 版本;TLS,全称 Transport Layer Security,则是 1999 年基于 SSL3.0 版本上改进而来的。官方建议弃用 SSL 而保留和采用 TLS,但是由于历史原因,SSL 仍然存在,而且很多人已经习惯 SSL 这个名词,因此现在索性就叫成 SSL/TLS。

SSL/TLS 的发展史,可以阅读文章《SSL/TLS发展历史和SSLv3.0协议详解》(
http://www.bewindoweb.com/271.html);关于两者的异同,推荐阅读文章《SSL vs TLS - What's the Difference?》(
https://www.globalsign.com/en/blog/ssl-vs-tls-difference)。


1.2 HTTPS 为什么?

肯定有不少同学不假思索:“当然是 HTTP 不安全,HTTPS 安全,所以选择 HTTPS 呗!”那么,HTTPS 比 HTTP“好”在哪里?

维基百科上对 HTTP 的解释如下:

设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。

HTTP 的发展是由蒂姆·伯纳斯-李于 1989 年在欧洲核子研究组织(CERN)所发起。HTTP 的标准制定由万维网协会(World Wide Web Consortium,W3C)和互联网工程任务组(Internet Engineering Task Force,IETF)进行协调,最终发布了一系列的 RFC,其中最著名的是 1999 年 6 月公布的 RFC 2616,定义了 HTTP 协议中现今广泛使用的一个版本——HTTP 1.1。

HTTP 协议是为了传输网页超文本(文本、图像、多媒体资源),以及规范客户端和服务器端之间互相请求资源的方法的应用层协议。在 1989 年最早推出了 HTTP 0.9 版本,而 1999 年公布的 HTTP 1.1 是到目前(2020 年)仍旧广泛使用的版本(引自《HTTP协议几个版本的比较》(
https://zhuanlan.zhihu.com/p/37387316))。

但是这个 HTTP 1.1 版本存在一个很大的问题-明文传输(Plaintext/Clear Text),这个问题在互联网时代的今天是致命的,一旦数据在公共网络中被第三方截获,其通信内容轻而易举地就被窃取了。

因此,HTTPS 应运而生,它被公认的三大优势有:

  1. 数据加密,防窃听
  2. 身份验证,防冒充
  3. 完整性校验,防篡改

啰嗦说一句:第 1 点确实是解决了 HTTP 明文传输;至于第 2 和第 3 点,其他一些应用层协议也会遇到(服务端被冒充、数据被篡改是网络传输中“较常见”的问题)。

再插一句题外话:2015 年推出的 HTTP 2 在 HTTP 1.1 的基础上有很大改进,其中一点就是 HTTPS。对 HTTP 2 有兴趣的同学推荐阅读《OPINION - HTTP versus HTTPS versus HTTP/2》(
https://www.tunetheweb.com/blog/http-versus-https-versus-http2/)、《HTTP/2 中的常见问题》(
https://halfrost.com/http2-frequently-asked-questions/)。



1.3 HTTPS 怎么做?

本文接下来将从 HTTPS 的 3 个优势展开说明,即:

  1. 数据加密,即 HTTPS 是怎么进行数据加密的。本章介绍 HTTPS 中的对称加密和非对称加密
  2. 身份验证,即 HTTPS 是怎么让客户端相信“发给我数据的服务端是我想要的服务器”。本章介绍 HTTPS 的 CA 和证书
  3. 完整性校验,即 HTTPS 是怎么做数据完整性校验以防止被篡改。本章介绍 HTTPS 的哈希

最后,为了整篇文章的完整性,还会增加以下几个内容:

  1. HTTPS 流程,即客户端和服务器端 HTTPS 通信全过程
  2. 实际问题,记录了笔者在实战中遇到 HTTPS 相关问题



II. 数据加密:HTTPS 的对称加密和非对称加密

相信不少同学会说"对称加密和非对称加密有什么好讲的?前者只有一把密钥做加解密;后者有两把密钥,公钥和私钥,互为加解密,公钥给对方,私钥自己用。HTTPS 两者都有。好了,这章可以结束啦。"

三个问题:

  1. HTTPS 为什么同时要有对称加密和非堆成加密两种加密方式?
  2. HTTPS 对称加密的密钥(本文称为 SK,下同)如何产生和传输?
  3. HTTPS 的有几套非对称加密?目的是什么?是否可以省略?

好,一个问题一个问题来。

问题 1:HTTPS 为什么同时要有对称加密和非对称加密两种加密方式?

默认各位同学已经知晓对称加密和非对称加密(了解基本原理即可),不清楚的同学推荐阅读知乎文章-《对称加密与非对称加密》(
https://zhuanlan.zhihu.com/p/30573146),文章最后指出了这两个加密方式的优缺点,原文如下:

(1)对称加密加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。

(2) 非对称加密使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。

那么解决办法有吗?有,文章随后说道:

(3)解决的办法是将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。

确实,HTTPS(最开始)就是这么做的!其思路大致如下:

1). 首先一定要明确HTTPS既有对称加密,又有非对称加密。-\> 2).由于对称加密性能高速度快,因此在传输数据时(也就是对话内容)采用对称加密。-\>3).但是对称加密的密钥SK,既没办法预先设置(密钥不可能只有一把,服务器端维护大量密钥也不具备可行性),因此只能选择在对话前通过网络协商出一把新的SK。-\>4). 为了确保SK的传输安全,使用非对称加密来协商SK。

HTTPS 的这种设计同时兼顾了安全和效率,赞叹先驱们的智慧!



问题 2:HTTPS 对称加密的密钥 SK 如何产生和传输?

通过第一个问题,我们知道了 HTTPS 分为2 个过程

  1. 协商对称加密密钥 SK 的非对称加密阶段,称为TLS 握手阶段
  2. 使用 SK 对数据(对话内容)进行对称加密的阶段,称为数据通信阶段

过程 2数据通信阶段:发送端首先用密钥 SK 对通信内容进行对称加密,接着通过网络传输出去;对端收到数据后,用 SK 先将数据解密,于是就得到了通信内容。这里笔者有一个问题尚未求证:在数据传输过程中,是否会对通信数据进行哈希以确保其不被篡改?姑且记一笔。

过程 1TLS 握手阶段:协商密钥 SK。网上关于这个知识点的文章有很多,然而一些已经过时,一些则不全。就协商密钥 SK 这块,笔者推荐《扫盲 HTTPS 和 SSL/TLS 协议[3]:密钥交换(密钥协商)算法及其原理》(
https://program-think.blogspot.com/2016/09/https-ssl-tls-3.html) 和《密钥协商机制》(
https://www.jianshu.com/p/b1d6996d2f51)。本文直接扔出结论,HTTPS 协商对称加密密钥 SK 的办法有很多种,介绍 3 种较为常见的办法:

  1. 基于非对称加密算法
  2. 基于专用密钥交换算法,常见有 DH、ECDH 等
  3. 基于共享的 secret,常见有 PSK,SRP 等

方法 1. 基于非对称加密算法

维基百科对于非对称加密算法的解释:公开密钥密码学(英语:Public-key cryptography)也称非对称式密码学(英语:Asymmetric cryptography)是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;一个用作加密,另一个则用作解密。使用其中一个密钥把明文加密后所得的密文,只能用相对应的另一个密钥才能解密得到原本的明文;甚至连最初用来加密的密钥也不能用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密;不同于加密和解密都使用同一个密钥的对称加密。虽然两个密钥在数学上相关,但如果知道了其中一个,并不能凭此计算出另外一个;因此其中一个可以公开,称为公钥,任意向外发布;不公开的密钥为私钥,必须由用户自行严格秘密保管,绝不透过任何途径向任何人提供,也不会透露给被信任的要通信的另一方。

非对称加密 RSA 协商密钥的办法,是 HTTPS(严格说是 SSL/TLS 协议)最早的办法,其过程如下:

  1. 客户端给服务端发送请求;
  2. 服务端返回客户端自己的公钥 PuK;
  3. 客户端产生本次对话的对称密钥 SK,并用 PuK 进行加密得到 SK_Enc 后传给服务端;
  4. 服务端收到 SK_Enc 后用自己的私钥 PrK 解密得到 SK;若成功,则返回客户端 OK,否则终止对话.
  5. 接下来,客户端和服务端的对话均用 SK 加密后传输。

方法 2:基于专用密钥交换算法

方法 1 是被大部分人熟知的,但是存在一个问题:如果服务端的私钥 PrK 泄露了,那么 HTTPS 所做的加密也就不安全了。

因此,就有了密钥交换算法(有说法是 keyless 方法)。方法原理笔者没有深究(数学功底有限,看到大量的公式证明还是会烦…),DH 和 ECDH 协商密钥算法大致过程如下:

ECDH 算法中的 A 和 B,在有的材料中也被称为PreMaster-Secret。最终协商得到的密钥 SK 也被称为Master Secret,也被称为Session Key

直接给出结论:ECDH 比 DH 算法更快,有说法是 10 倍;而且 ECDH 比 DH 更难破解,可行性更好。推荐阅读《Elliptic Curve Cryptography: ECDH and ECDSA》(
https://andrea.corbellini.name/2015/05/30/elliptic-curve-cryptography-ecdh-and-ecdsa/)、《完全吃透 TLS/SSL》(
https://juejin.im/post/5b305758e51d4558ce5ea0d9) 更进一步了解 DH 和 ECDH 算法。

方法 3:基于共享的 secret

这类做法就是在客户端和服务端预设好对称加密密钥,握手阶段只需要传递类似钥匙 id 即可。代表算法有 PSK。



问题 3:HTTPS 的有几套非对称加密?目的是什么?是否可以省略?

直接给出答案:2 套非对称加密

第一套用于协商对称加密密钥 SK(问题 2 一直在讨论的内容);第二套用于数字证书签名加密(接下来一章会详细讨论 CA 证书)。这两者的区别是:前者是服务器端(如果是双向验证的话,客户端也会有一套非对称加密公私钥)产生的。私钥在服务端上;后者是 CA 机构产生的,私钥在 CA 机构那边。

并且,这 2 套都不可以省略。(这个说法略不严谨,但是在实际操作中,确实都不建议省略。)




III. 身份验证:HTTPS 的证书

笔者认为,对大部分程序员来说,工作中遇到的 HTTPS 相关问题,80%~90%都是跟证书相关的。因此,了解证书非常关键!

3.1 证书是什么?

解释这个问题之前,先看几个关键词:CACA 机构数字证书数字签名(证书)指纹(CA)证书HTTPS 证书SSL/TLS 证书

理一理上面这些关键词之间的关系:

  • CA,CA 机构:机构/组织概念。
  • 数字证书,(CA)证书,HTTPS 证书,SSL/TLS 证书:CA 签发的数字证书。
  • 数字签名,(证书)指纹:CA 签发的证书的内容之一,一段加密的密文。

智库百科对数字证书的解释是:

数字证书也称公开密钥证书,是指用于电子信息活动中电子文件行为主体的验证和证明,并可实现电子文件保密性和完整性的电子数据。数字证书是一个经证书认证中心(Certification Authority,简称 CA)发行的文件。

数字证书包含有行为主体信息和证书认证机构的数字签名。数字签名是指以电子形式存在,可依附在电子文件中用于辨识电子文件的签署者及表示对该电子文件内容负责所使用的电子数字标识。

抓重点:数字证书用于主体身份验证。

首先,数字证书=主体信息+数字签名

Windows 下,我们可以在 Chrome 浏览器上点击地址栏的“锁”标记后会出现下拉框,接着点击“证书”即可看到通过 HTTPS 访问该服务器时的数字证书。具体操作如下:

  • 证书“常规”页面,是关于该证书的一个笼统介绍,包括签发该证书的 CA 机构、该证书绑定的域名(颁发给)、证书有效期。
  • 证书“详细信息”页面,以键值对形式展示了这张证书的完整内容。(后文将详细介绍证书内容)
  • 证书的“证书路径”,以层级结构展示了从该证书绑定的域名一直到根证书的路径,这就是证书链。(后文会展开介绍证书链)

看完了 Chrome 浏览器上的证书,在让我们通过 Wireshark 抓包来来看看数字证书:

对比 Chrome 上看到的数字证书和 Wireshark 抓包得到的数字证书,可以看到两者呈现的证书内容是一致的。总结来看,一张完整的数字证书包括:

  • 主体的必要信息:版本(version)、序列号(serialNumber)、签名算法(signatureAlgorithm)、颁发者(issuer)、有效期(validity)、使用者(subject)、公钥信息(subjectPublicKeyInfo)
  • 主体的扩展信息(extension):如密钥标识符、证书策略等
  • 数字签名(signature),也称指纹

抽象为下图:



3.2 为什么是数字证书呢?

HTTPS 已经对通信数据进行了加密,为什么还要验证身份?说好的“人与人之间最基本的信任呢?”

这还不是因为各路黑客、骇客们总是在制造各种攻击吗?其中一个大名鼎鼎的中间人攻击(Man-In-The-Middle Attack,MITM 攻击),简单的讲,“中间人”在客户端和服务端都不知情的情况下,夹在双方之间窃听甚至篡改通信信息,过程见下图(图引自《HTTPS中间人攻击实践(原理·实践》(
https://www.cnblogs.com/lulianqi/p/10558719.html)):

在 HTTPS 的握手阶段,一端向对端发送请求,对端返回自己的公钥;而一端未验证对方的身份和公钥,直接协商密钥。“中间人”看到了这个漏洞,夹在中间截获了对端的公钥,替换成了自己的公钥。正是这步“拿错了公钥”或者说“信错了对端”,使得 HTTPS 为加密(密钥协商的非对称加密和通信数据的对称加密)所做的努力全部泡汤。

可见,在 HTTPS 中,“确保对端身份正确”即“确保拿到的公钥正确”,而在网络通信中所谓的“身份”,一般指的是通信一端的域名IP 地址甚至是Mac 地址。所以,数字证书同时包含了通信一端的身份信息公钥信息

但是数字证书会在网络中传输(由被要求验证身份的一端通过网络传给另一端),这就意味着证书也可能会被窃取篡改。这个时候权威的 CA 机构就出马了,他想了个办法:加了一个“防伪标识”— 数字签名。具体做法如下:

signature = RSA(PriKey_CA, Hash(message))

这里啰嗦几句:数字签名生成过程是首先对原文作哈希,把一段不定长的文本映射成固定长度的字符空间,接着再用 CA 机构的私钥对这段定长字符做加密。大大提高了整体的运算效率。



3.3 证书是怎么工作的?

要了解证书是怎么做“身份验证”,即“防冒充”,得从 2 个角度来说明:

  • 申请证书,即需要被验证身份的一端,需要申请一份能够验证自己身份的证书
  • 验证证书,即需要验证对方身份的一端,拿到证书后验证对端的身份

请注意,这里有一个前提:这张证书必须是由权威 CA 机构颁发的,且尚在有效期内;或者是一张信任的私人证书

申请证书

CA 机构和证书的分类本文不讨论,推荐阅读《细说 CA 和证书》(
https://www.barretlee.com/blog/2016/04/24/detail-about-ca-and-certs/),这里我们讨论正规权威 CA 机构签发的证书,至于是 DV、OV 还是 EV,只是安全强度问题,工作原理是一样的。

总结申请证书的过程:用户向 CA 机构提交自己的信息(如域名)和公钥(用户自己生成的非对称加密公钥,用于 TLS 握手阶段和另一端协商密钥用),CA 机构生成数字证书,如下图:

验证证书

收到对端发过来的证书,执行证书申请的“逆过程”即可,总结如下图:

接受证书的一端先对除数签名的其他部分做一次相同的哈希算法(证书中指明了哈希算法),得到这段文本的哈希映射,记作 H1;获取 CA 机构的公钥对数字签名属性做解码,得到了 CA 机构计算出的哈希映射,记作 H2。对比 H1 和 H2 两个字符串是否严格相等,若是,代表该证书的信息未被篡改,证书有效;否则,证书内容被篡改,证书无效。

若证书有效,接受端会再进行对端的身份校验(验证域名),若身份验证通过,接收端会拿证书上的公钥(也是对端自己生产的非对称加密公钥)加密接下来整个 TLS 握手阶段的信息之后,发送给对端。

这个过程中有一个问题:CA 机构的公钥怎么获取?

回答:提前内置。

众所周知,操作系统和浏览器在软件安装阶段会在其特定目录下放置一堆的证书。如 Windows 的根证书管理在 certmgr 下:

这些证书都有个特点:权威 CA 机构发布的根证书(Root Certificate)。根证书有几个特点:

  • 没有上层机构再为其本身作数字签名
  • 证书上的公钥即为 CA 机构发布的公钥
  • 权威 CA 机构的自签证书

而这些根证书会跟很多软件,包括操作系统、浏览器一起被安装到用户设备上。即使没有被提前安装好,这些根证书也可以在 CA 机构的官网上获取得到。

目前全球大型权威 CA 机构有 Symantec、GeoTrust、Comodo 以及 RapidSSL 等,并且这些机构颁发的 SSL 数字证书,在市场的占有率是非常的高。(节选自《SSL证书颁发机构有哪些》(
https://www.anxinssl.com/5341.html))

本地被内置了这么多的根证书,那要怎么知道我这份证书应该要用哪一个根证书来验证呢?

回答:证书信任链。

在信任链上有 3 类证书:根证书中介证书用户证书。根证书前文已有说明,用户证书就是对端发过来的证书,或者说是用户向权威 CA 机构绑定了自己身份(主要指域名)和自己公钥的证书。中介证书可以理解由权威 CA 机构委派的代理机构签发的数字证书,推荐阅读《What is an intermediate certificate?》(
https://sg.godaddy.com/en/help/what-is-an-intermediate-certificate-868)。中介证书或者说是中介机构的存在是为了保证根证书的密钥的安全性。

细心的同学仔细看一看 certmgr 会发现有一个分类是“中间证书颁发机构”,这里存放的就是中介证书。用户证书绝大多数是通过权威的 CA 机构的代理中介机构颁发。

这么来说,根据对端发来的用户证书寻找对应的根证书岂不是更困难了?

自问自答:这是一个在树形数据结构中,从叶子节点搜索根节点的过程,直接一个最原始的深搜(DFS)不就可以了嘛?举例说明,如下图(引自Wikipedia-Chain of trust)(
https://en.wikipedia.org/wiki/Chain_of_trust):

  1. 从用户证书开始。
  2. 记“Issuer”字段的值为 i1,搜索本地证书,寻找由“Subject”为 i1 的证书。
  3. 若没有找到,结束返回证书无效;否则,跳到步骤 4)。
  4. 判断该证书的 Issuer 值是否等于 Subject 值。
  5. 若是,则该证书是根证书,结束返回该证书;否则跳到步骤 6)。
  6. 以该证书开始,跳转 2)(继续搜索)。

更多关于信任链的知识点,推荐阅读《What is the SSL Certificate Chain?》(
https://support.dnsimple.com/articles/what-is-ssl-certificate-chain/)



3.4 证书怎么样?

相信不少同学或多或少接触过证书文件,比如.pem、.crt、.cer、.key 等,于是问题就来了:

“为什么有这么多不同后缀名的证书啊?他们有什么联系和区别?”

回答这个问题要从 3 个层面来分析:

  • 证书标准
  • 证书编码格式
  • 文件扩展名

证书标准

数字证书的格式普遍采用的是 X.509 国际标准,维基百科对于 X.509 解释如下:

X.509 是密码学里公钥证书的格式标准。X.509 证书已应用在包括 TLS/SSL 在内的众多网络协议里,同时它也用在很多非在线应用场景里,比如电子签名服务。X.509 证书里含有公钥、身份信息(比如网络主机名,组织的名称或个体名称等)和签名信息(可以是证书签发机构 CA 的签名,也可以是自签名)。对于一份经由可信的证书签发机构签名或者可以通过其它方式验证的证书,证书的拥有者就可以用证书及相应的私钥来创建安全的通信,对文档进行数字签名。

X.509 是 ITU-T 标准化部门基于他们之前的 ASN.1 定义的一套证书标准。

证书的编码格式

X.509 标准的证书文件具有不同的编码格式:PEM 和 DER。

  1. PEMPEM,全称 Privacy Enhanced Mail,以文本格式存储,以 -----BEGIN
    XXX-----开头、-----END XXX-----结尾,中间内容是 BASE64 编码数据。其文本内容大概如下:html -----BEGIN CERTIFICATE----- Base64编码过的证书数据 -----END CERTIFICATE-----

通常,PEM 格式可以存储公钥、私钥、证书签名请求等数据。查看 PEM 格式证书的信息一般采用如下命令:

openssl x509 -in xxx.pem -text -noout

Apache 和 Nginx 服务器偏向于使用这种编码格式.

  1. DERDER,全称 Distinguished Encoding Rules,以二进制存储,因此文件结构无法直接预览,只能通过如下命令查看:html openssl x509 -in xxx.der -inform der -text -nooutDER 格式也可以存储公钥、私钥、证书签名请求等数据。Java 和 Windows 应用偏向于使用这种编码格式。

当然同一 X.509 证书的不同编码之间可以互相转换:

  • PEM 转为 DER:
openssl x509 -in xxx.pem -outform der -out xxx.der
  • DER 转为 PEM:
openssl x509 -in xxx.der -inform der -outform pem -out xxx.pem

文件扩展名

不同的扩展名可以分为以下几类:

  • 证书:存放数字证书,X.509 标准,格式可能是 PEM 或 DER。.crt、.cer
  • 密钥:用来存放一个 RSA 公钥或私钥,这类文件不是 X.509 标准,但是是 PEM 或 DER 格式。后缀名有.key。
  • 证书+密钥:可同时存放证书和 RSA 公钥/.pem、.der、.p12
  • 证书请求:并不是证书,而是证书签名请求。csr



IV. 完整性校验:HTTPS 的哈希

哈希,键值对数据结构,通过哈希函数把一个空间映射到另一个空间。非常好用的一个工具,而且哪哪儿都有它的影子,比如负载均衡的一致性哈希、密码学中用于信息加密或数据校验的各种哈希(SHA、MD5 等)、二维空间定位的 GeoHash、对象相似度的 SimHash 等等。

HTTPS 的的哈希一共用在 2 个地方:


4.1 证书的数字签名

具体做法在上文证书一章节已经说过,不再赘述。在这里使用哈希的目的主要是为了减少非对称加密算法 RSA 在长文本上的开销。


4.2 对称加密的 Message Digest

在数据通信阶段,SSL/TLS 会对原始消息(message)做一次哈希,得到该消息 message 的摘要,称为消息摘要(Message Digest)。对端接受到消息后,使用协商出来的对称加密密钥解密数据包,得到原始消息 message;接着也做一次相同的哈希算法得到摘要,对比发送过来的消息摘要和计算出的消息摘要是否一致,可以判断通信数据是否被篡改。


V. HTTPS 通信流程

到此,HTTPS 涉及到的关键问题基本都覆盖了。本章总结整个 HTTPS 的通信过程:


补充说明几点:

5.1 协商密钥:客户端/服务端随机数、Client/Server Key

在加密一章节介绍的 ECDH 是停留在原理层面,实际中密钥协商除了 PreMaster-Secret(即 Client/Server Key)之外,还有客户端和服务端随机数参与,参考文章《Https:TLS 握手协议》(
https://www.jianshu.com/p/3d7046932040),引用文中的图来自展示实际 ECDH 秘钥协商的做法:


5.2 Change Cipher Spec

Change Cipher Spec是通知对方需要加密参数。文章《TLS 改变密码标准协议(Change Cipher Spec Protocol)》 (
https://www.cnblogs.com/bonelee/p/10404733.html) 指出:

SSL 修改密文协议的设计目的是为了保障 SSL 传输过程的安全性,因为 SSL 协议要求客户端或服务器端每隔一段时间必须改变其加解密参数。当某一方要改变其加解密参数时,就发送一个简单的消息通知对方下一个要传送的数据将采用新的加解密参数,也就是要求对方改变原来的安全参数。

SSL 修改密文协议是使用 SSL 记录协议服务的 SSL 高层协议的 3 个特定协议之一,也是其中最简单的一个。协议由单个消息组成,该消息只包含一个值为 1 的单个字节。该消息的唯一作用就是使未决状态复制为当前状态,更新用于当前连接的密码组。为了保障 SSL 传输过程的安全性,双方应该每隔一段时间改变加密规范。


5.3 Encrypted Handshake Message

Encrypted Handshake Message作用就是确认协商出来的对称加密密钥 SK 的正确性,在客户端和服务端协商得到对称加密密钥 SK 之后,互相给对方发了一条用 SK 加密的消息,如果这个加密的消息被解密校验成功,那么就说明对称加密密钥 SK 是正确的。


5.4 单向验证和双向验证

本章全部所探讨的案例都是基于单向验证,即客户端向服务端请求证书、验证服务端身份。在一些实际场景中,对安全性的要求更高,有服务端要求验证客户端的身份,即双向验证。双向验证在单向验证基础上,增加“在服务端发送证书之后,向客户端发送‘请求证书’请求,接着验证客户端身份”这个步骤。参考下图(图片出处不查):



VI. HTTPS 实战问题记录

问题:HTTPS 是否需要做域名劫持?

没必要。

原因如下:https 在证书校验这步,客户端除了通过对比数字签名来校验证书的有效性,还会比较证书上的域名是否与自己要访问的域名一致。因此,只要服务器的证书是可信的且客户端不跳过“证书验证”这个步骤,https 能够防止域名劫持。

笔者在实际中做过防止域名劫持的工作,具体做法是:首先,客户端向可信赖的域名服务器请求域名对应的 IP 地址;接着,客户端用 IP 替换域名进行网络请求。被称为HTTPS 的 IP 直连。但在实际中会遇到了一个问题:域名身份不对。根本解决方案是:1).书校验时,选择自己定义的 hostname 进行校验;2).证书校验前,把 URL 的 IP 替换回域名。在很多语言实现中,解决方案更为简单:在请求头部的增加 host-name 字段,值填入域名即可。

问题:"x509: certificate signed by unknown authority"

这个问题是客户端拿到了服务器的证书要进行身份验证,但是通过证书信任链策略发现中间断了,搜索不到根证书。说白了就是客户端本地没有签发这个用户证书的根证书或中介证书。

实际中的解决办法有:1). 缺啥装啥,没有根证书/中介证书,那就安装上;2).跳过证书身份验证这步。GoLang 中跳过身份验证的实现:

client := &http.Client{    Transport: &http.Transport{        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},    }}

问题:https 项目中,服务端怎么管理证书和 RSA 密钥?

看情况。

如果是正规的公司,一般会有统一的接入层帮忙做掉,后台开发程序员只需要关心自己的业务逻辑即可。运维同事或者是负责接入层开发的同事们会定期更新证书,负责帮忙做 HTTPS 的 RSA 管理。而且这些证书都是由正规权威 CA 机构签发的,普遍的客户端设备上都预置了对应的根证书。
如果是个人,一般没有条件负担 HTTPS 证书费用,因此可以选择自己签发自签证书,或者到免费的证书签发机构申请证书。但是这类证书要求在客户端上同步安装对应的根证书。

问题:https 项目中,客户端的根证书要提前安装吗?

上一个问题中已经提到了:

  • 如果是正规权威 CA 机构签发的证书,一般不需要提前安装;
  • 如果是私人签发的证书,需要提前安装。

但是,笔者在边缘计算设备上开发时发现,比如摄像头这类的“tinny os”上,是一个“阉割”版本的 Linux 系统,因此没有安装任何根证书。在这类设备上做 https 通信,一定需要提前安装根证书才行。