II. 身份验证:HTTPS的证书

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

2.1 证书是什么?

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

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


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

数字证书也称公开密钥证书,是指用于电子信息活动中电子文件行为主体的验证和证明,并可实现电子文件保密性和完整性的电子数据。数字证书是一个经证书认证中心(Certification Authority,简称CA)发行的文件。
数字证书包含有行为主体信息和证书认证机构的数字签名。数字签名是指以电子形式存在,可依附在电子文件中用于辨识电子文件的签署者及表示对该电子文件内容负责所使用的电子数字标识。

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

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

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

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

Wireshark抓包获取到的证书


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

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

抽象为下图:

数字证书


2.2 为什么是数字证书呢?

HTTPS已经对通信数据进行了加密,为什么还要验证身份?说好的“人与人之间最基本的信任呢?”
这还不是因为各路黑客、骇客们总是在制造各种攻击吗?(捂脸)其中一个大名鼎鼎的中间人攻击(Man-In-The-Middle Attack,MITM攻击),简单的讲,“中间人”在客户端和服务端都不知情的情况下,夹在双方之间窃听甚至篡改通信信息,过程见下图(图引自《》):

中间人攻击

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

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

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

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


2.3 证书是怎么工作的?

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

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

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

申请证书
CA机构和证书的分类本文不讨论,推荐阅读《》,这里我们讨论正规权威CA机构签发的证书,至于是DV、OV还是EV,只是安全强度问题,工作原理是一样的。

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

证书申请

验证证书

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

证书验证

接受证书的一端先对除数签名的其他部分做一次相同的哈希算法(证书中指明了哈希算法),得到这段文本的哈希映射,记作H1;获取CA机构的公钥对数字签名属性做解码,得到了CA机构计算出的哈希映射,记作H2。对比H1和H2两个字符串是否严格相等,若是,代表该证书的信息未被篡改,证书有效;否则,证书内容被篡改,证书无效。
若证书有效,接受端会再进行对端的身份校验(验证域名),若身份验证通过,接收端会拿**证书上的公钥(也是对端自己生产的非对称加密公钥)**加密接下来整个TLS握手阶段的信息之后,发送给对端。

这个过程中有一个问题:CA机构的公钥怎么获取?
回答:提前内置。

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

certmgr下的证书

受信任的根证书办法机构”下证书都有个特点:权威CA机构发布的根证书(Root Certificate)。根证书有几个特点:

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

而这些根证书会跟很多软件,包括操作系统、浏览器一起被安装到用户设备上。即使没有被提前安装好,这些根证书也可以在CA机构的官网上获取得到。
目前全球大型权威CA机构有Symantec、GeoTrust、Comodo以及RapidSSL等,并且这些机构颁发的SSL数字证书,在市场的占有率是非常的高。(节选自《》)

本地被内置了这么多的根证书,那要怎么知道我这份证书应该要用哪一个根证书来验证呢?
回答:证书信任链。
在信任链上有3类证书:根证书中介证书用户证书。根证书前文已有说明,用户证书就是对端发过来的证书,或者说是用户向权威CA机构绑定了自己身份(主要指域名)和自己公钥的证书。中介证书可以理解由权威CA机构委派的代理机构签发的数字证书,推荐阅读《》。中介证书或者说是中介机构的存在是为了保证根证书的密钥的安全性。
细心的同学仔细看一看certmgr会发现有一个分类是“中间证书颁发机构”,这里存放的就是中介证书。用户证书绝大多数是通过权威的CA机构的代理中介机构颁发。

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

自问自答:这是一个在树形数据结构中,从叶子节点搜索根节点的过程,直接一个最原始的深搜(DFS)不就可以了嘛?举例说明,如下图:

信任链
i1i1IssuerSubject

更多关于信任链的知识点,推荐阅读《What is the SSL Certificate Chain?》


2.4 证书怎么样?

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


X.509PEMDER
-----BEGIN XXX----------END XXX-----PEMPEMDER
X.509
openssl x509 -in xxx.pem -outform der -out xxx.deropenssl x509 -in xxx.der -inform der -outform pem -out xxx.pem


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

X.509PEMDER.crt.cerX.509PEMDER.key.pem.der.p12csr