HTTPS证书:原理、签发、部署和验证

2020-04-08

前言:HTTP和HTTPS

我们不想把话说得太绝对,但是,不管怎么说,你几乎找不到另外一种比HTTP应用得更广泛更流行的应用层协议.现在,打开你的智能手机,订外卖也好,查天气也好,浏览网页也好,看短视频也好,对应实现这些功能的App,其实都很难离开HTTP协议.

HTTP协议啊,它默认传输的是明文,明文什么意思呢?就是内容是什么,它就怎么传输,怎么理解呢?假如说浏览器和服务器通信,浏览器问小明:你的账号和密码是多少,小明告诉浏览器,我的账号是xiaoming,我的密码是secret123,然后浏览器就直接隔空跟服务器说:账号xiaoming,密码secret123,浏览器和服务器中间链路上的任何节点,都能看到这条消息,都能看到小明的账号和密码.

小明登录GitHub时浏览器发送给服务器的信息

小明登录GitHub时浏览器发送给服务器的信息

如果浏览器单纯地使用HTTP协议跟服务器通信,那中间人就会看到小明的账号和密码,于是为了使浏览器传输给服务器的内容和服务器传回来给浏览器的内容不被第三方看到,有了HTTPS,也就是HTTP over TLS,叫做「基于TLS的HTTP协议」,TLS是一系列的安全规程,是协议,它规定了浏览器和服务器之间怎么加密要通信的内容,怎么确定对方是真实.HTTP在TLS的加持下,变得更加安全了,具体来说,浏览器传输到服务器的内容,和服务器传回来的内容,不会再被别人看到了,也不会被伪造和篡.这里的「不会被别人看到了」是指,在中间人眼里,浏览器和服务器传输的内容就是一系列的乱码,从中完全看不出小明的账号是多少或者密码是多.就跟行内人士沟通用黑话,外行完全听不懂是一个概念.

这篇文章将会涉及哪些内容

首先正如标题,肯定是围绕着HTTPS的,更具体地,我们侧重于讲解和TLS证书有关的内容,TLS证书即满足.509标准的证书,如果你没听说过「证书」这个概念,那我们先简单的告诉你,在这里,或者说在计算机的世界中,所谓的「证书」其实不过是一个计算机中的文件,上面记录着一些信息、数据、加密数据和散列.这里所说的「文件」和我们熟悉的,譬如说,.docx文件,.mp3文件,.pdf文件是同一个概念.

首先我们会简单地介绍TLS所涉及到的密码学原理——公钥加密,以及公钥加密得以施行所依赖的CA(涉及到一点点公钥基础设施),以及和信任链有关的问题,然后我们会讲怎样自己签发证书或者申请证书,以及怎样部署证书到自己的网站服务器上从而为自己的网站开通HTTPS,以及怎样验证HTTPS证书,以及怎样查看HTTPS证书所蕴含的信息.

公钥加密

我们只会介绍基本的概念,而不会引入太多的数学讨论,也就说不会引入太多的数学公式,要想了解更多,您可以直接翻看文末的「参考链接」.

公钥和私钥只不过分别是数学上的一个「整数」,没错其实就是数学课本上说的整数,如果说不是一个整数,那至多是若干个整数,由若干个整数就能表示.我们把公钥记做$p$,把私钥记做$q.现在你只需知道

$$ p, q \in \mathbb{Z}. $$

有这么一个场景,David和Bob有一天聚在一起,David和Bob希望实现一种方法,使得今后Bob能给David传纸条,而纸条上的内容即使被中间递送纸条的人看见了,递送纸条的人也看不懂纸条上写的内容是什么意.David想到了自己学过公钥加密的知识,Bob其实也有一些公钥加密的知识,于是David和Bob决定使用一种叫做RSA的密码(密码是加密解密方式的一系列约定,并不是说「RSA」就是密码).

首先David随机挑选了一个非常非常大的质数,叫做$q$,David把$q$选做私钥,然后David翻看RSA的手册,按照手册中描述的方法,由私钥$q$计算出或者说生成出公钥$p$,按照RSA手册中描述的,有一个数学运算,叫做$f_e$,把明文转换为整数$d$之后,可以这样计算出加密后的数据$e$

$$ e = f_e(p, d) $$

这就是加密的过程,$f_e$是一套运算过程,也是加密算法,$d$可以是明文,比如说账号和密码,而如果拿到了$e$,是看不出关于$d$的任何信息的,是不能够从密文$e$当中看出任何和明文(账号密码)有关的信息的,要想由$e$解密出$d$,只有知道私钥$q$的人能做到,下面是解密过程

$$ d = f_d(q, e) $$

式中$e$是密文,$q$是私钥,而$d$就是前面那个式子中的$d$,也就是明.

现在David告诉Bob公钥$p$的值,当然了David和Bob都知道$f_e$和$f_d$的具体表达式,也就是都知道有了公钥如何加密,有了私钥如何解密,那么从今以后,Bob要给David发送信息$d_1$,可以先用加密算子$f_e$和David的公钥计算出密文$e_1$

$$ e_1 = f_e(p, d_1) $$

然后把密文$e$写在信中,交给任何人递送,David收到密信$e_1$后,用自己的私钥$q$和知道的RSA密码套件约定的解密方法$f_d$解密,得到原文$d_1$

$$ d_1 = f_d(q, e_1) $$

以上就是公钥加密和私钥解密的基本概.加密算子和加密算子必须要足够健壮使得仅仅根据密文$e$很难算出明文$d$.

公钥递送过程中所面临的问题

前面我们说David和Bob先是聚在一起,然后David才把公钥$p$交给Bob,那能不能David和Bob不见面,通过中间人把公钥$p$递送给Bob呢?

不行.

假设中间人叫做Mallory,它怀着恶意,当David委托中间人Mallory把公钥$p$递送给Bob时,Mallory假装答应,随后Mallory并没有把David的公钥真正地递送给Bob,而是把自己的公钥$p_m$(Mallory的公钥)递送给Bob,而当Bob收到$p_m$后,仅仅根据RSA的知识,并且在Bob对David的公钥$p$和David的公钥的特征$\lambda(p)$一无所知的情况下,是无法分辨Mallory的公钥$p_m$和David的公钥$p$有任何差别的,于是Bob会毫不怀疑地把Mallory的公钥$p_m$当成是David的公钥$p$.

然后Bob会给David写信,信的内容是$d$,首先Bob加密$d$,用的当然是Mallory的公钥:

$$ e = f_e(p_m, d) $$

然后Bob把密件$e$交给Mallory递送,而公钥是由私钥生成的,Mallory当然有和$p_m$对应的私钥$q_m$,于是Mallory就可以轻松地解密$e$,看到明文$d$的内容:

$$ d = f_q(q_m, e) $$

这种情况也被叫做「中间人攻击」(MITM),是MITM所有攻击方式中比较简单的,这个例子告诉我们,公钥$p_{\text{david}}$必须真的是收件人主体David的,而不能是任何其他人,更不能是中间人Mallory的,否则公钥加密得到的密文就会被David之外的人解密,失去效.公钥加密对公钥的递送有很高的要求,尤其是,公钥在递送中切不可被篡改、替换或伪造,否则就会使公钥加密方法失去效果,造成很严重的信息泄露问题.

通过第三方签名的方式来解决公钥递送问题

德高望重的Viktor

就拿我们前面举的这个例子来说,David无法把公钥$p$远程地委托中间人Mallory递送给Bob,就算Bob收到了一个来源不明的私钥$p_x$,仅根据$p_x$,Bob无法确定$p_x$就是David的公钥$p$,因此也不能放心地使用来源不明的自称是David的公钥$p_x$给David发送密信.

设想有这么一位德高望重的好朋友他叫Viktor,Viktor受David和Bob的信任,并且David和Bob之前都跟Viktor见过面,David、Bob和Viktor都很确定自己有着真实未被篡改的Viktor的公钥$p_v$.

散列函数与散列值

继续说下去之前,必须要知道散列函数$h: \mathbb{Z} \mapsto \mathbb{D}$,其中$\mathbb{D} \subset \mathbb{Z}$,$\mathbb{D}$是有限个整数组成的集.对于$h$,我们只需知道,如果$x \neq y$那么几乎可以确定$h(x) \neq h(y)$,并且几乎不可能在仅仅知道$h(x)$的情况下以任何方式计算出$x$,特例发生的概率大概不超过百万分之.于是,我们把$h$叫做「散列函数」(Hash Function, hashfunc),而把$h(x)$叫做$x$的「散列值」(Hash Value, hashval).

给公钥「签名」

为了即使通过中间人Mallory也能让Bob收到David的真正的公钥$p_{\text{david}}$,David和Viktor进行一次会面,Viktor给David的公钥签名,然后把Viktor把「签名」给David,具体是这样的:

  1. 首先Viktor计算David的证书$p_{\text{david}}$的散列值$h(p_{\text{david}})$.
  2. 然后Viktor用自己的私钥对$h(p_{\text{david}})$加密,Viktor计算$h_e = f_d(q_v, h(p_{\text{david}}))$,你只需知道$f_d$和$f_e$是可以互相转化.这里的$h_e$就是「签名」,就是David的公钥$p_{\text{david}}$的签名.

David拿到了Viktor给自己证书的签名$h_e$,就可以放心大胆地,把自己的公钥$p_{\text{david}}$交由任何人递送给Bob了,仅仅要求,在递送公钥$p_{\text{david}}$时,必须连同签名一起

$$ \text{David} \rightarrow \text{Mallory}([p_{\text{david}}, h_e]) \rightarrow \text{Bob} $$

Bob收到$[p_x, h_{ex}]$后,要验证$p_x$是不是$p_{\text{david}}$其实也很简单:

  1. Bob首先解密$h_{ex}$,Bob计算$h_x = f_e(p_v, h_{ex})$,也就是利用Viktor的公钥$p_v$和$f_d$的反函数$f_e$来由$h_{ex}$反算出$h_x$,如果一切没问题,现在得到的$h_x$应该等于Viktor之前计算的$h(p_{\text{david}})$

  2. Bob计算$h_y = h(p_x)$,相当于Bob也对这个公钥做一次散列运算,就跟Viktor对David的公钥做散列运算一样,如果Mallory没做任何篡改,那么肯定会有$h_y=h_x$

因为如果Mallory篡改了$p_x$,使$p_x$不再等于$p_{\text{david}}$,那么$h(p_x)$也就不再等于$h_(p_{\text{david}})$,Mallory很难通过篡改$h_e$来使得$h_e$解密后得到的$h_d$等于篡改了的$p_x$的散列值$h(p_x)$,因此Mallory如果对递送的公钥或者公钥签名做了任何动作,一定会被发现.

我知道上面这个过程多少有些抽象,为此,我画了一幅图,描述了签名、递送和验证的过程:

签名和验证

签名和验证

图中对签名的描绘很好理解,而在验证过程中的第3步,Bob首先对ehashval x解密,就相当于签名图示中三角形△所示的步骤,然后对data x计算散列值,相当于签名图示中圆形○所示的步骤,如果数据在递送中没有被串改,解密ehashval x得到的hashval x应该等于由数据data x算出来的散列值hashval d.

希望你已经大概理解了签名和验证的概念.

CA、HTTPS证书和信任链

在介绍HTTPS证书如何实现客户端和服务器之间的双向加密之前,我想先讲另外一个例子:

网站的内容是以文件的形式存储在服务器中的,浏览器要打开一个网站,就要和提供这个网站的内容的服务器建立连接,譬如说,我想打开cloudflare.com这个网站,那么

打开一个网站之前要先知道它的IP地址

打开一个网站之前要先知道它的IP地址

那么客户端会首先尝试查询cloudflare.com,也就是网站的域名,所对应的IP地址,这里得到的cloudflare.com的IP地址是104.17.176.85,然后客户端才向104.17.176.85这台服务器请求cloudflare.com的内.域名cloudflare.com和IP地址104.17.176.85的关系,就像是建筑名和详细地址的关系.

但是要注意到这么一点,假使我们打开的是一个银行网站www.abank.com,首先客户端会向DNS服务器发起请求,询问www.abank.com的IP地址是多少,DNS服务器回答了正确的IP地址,但是无论是DNS查询的请求还是响应都是要经过中间人Mallory,Mallory把DNS服务器返回的结果(www.abank.com, 104.17.176.85)中的IP地址字段换成自己制作的假冒的银行网站的服务器(www.abank.com, 52.17.17.2),用户的客户端接着就会把52.17.17.2这个服务器看成是银行网站www.abank.com的服务器,就会打开假冒的银行网站而不是正规的银行网站,用户接下来输入的账号和密码都会传到恶意的中间人Mallory的假冒银行网站的服务器52.17.17.2而非正规的银行网站的服务器104.17.176.85,从而造成用户的账号密码泄.打个比方,就好像是张三(无辜)问路人丙(恶意)银行在哪我要去存钱,结果路人丙给张三指路指向了一家骗子机构,张三信以为真那是银行,于是张三去了骗子机构存钱.

HTTPS证书是为了防止刚才说的这种事情的发.具体地,HTTPS协议能够保证:当张三使用HTTPS协议打开银行网站www.abank.com,如果客户端连接到的是骗子的服务器而不是正规的银行网站的服务器,那张三一定会被告.HTTPS就是那个好心的路人甲,当路人丙告诉张三的是骗子机构而非正规的银行机构时,跳出来揭穿路人丙并提醒张三的路人甲.

浏览器提示DNS记录可能已经被篡改

浏览器提示DNS记录可能已经被篡改

HTTPS协议是怎样做到这一点的?HTTPS协议是根据什么来判断:假如说服务器x声称它能够提供网站domainx.com的页面,那服务器x是否真的是domainx.com的合法内容提供者?

回顾上一节,我们说到,即使David的公钥仍然通过不可信的中间人Mallory递送,只要公钥和公钥的签名一起递送,那么Bob收到数据后,还是可以拿Viktor的公钥来验证Viktor对公钥的签名,并且如果公钥或者签名在递送中被篡改了,一定会验证失败,这从而确保了(与签名一起递送的)公钥在递送过程中的不可篡改性(至少,如果篡改了会有提示,可看做是递送失败.也就是说,通过引入第三方的签名机构(所有人都有这个第三方签名机构的公钥),可以实现在不可靠的信道上递送敏感数据,同时还可确保敏感数据不被篡改(完整性.一句话说就是,这种基于权威第三方的签名机制可以一定程度上保证数据的完整性(至少当数据完整性被破坏时会有某种告警机制).

那为什么我们会提到这个通过权威第三方签名来保证数据完整性的例子?事实上,一个服务器要想向别人证明自己是domainx.com的合法内容提供者,按照我们上面说到的用权威第三方签名来确保数据完整性的例子,它需要有主体是domainx.com的一对公私钥(domainx.com对应David),以及CA的对domainx.com的证书(证书可以简单地看做公钥附上一些必要的主体身份信息)的签.浏览器和服务器建立连接,并且浏览器告知服务器要请求的域名(比如说是domainx.com),服务器为了证明自身提供相应内容的合法性,会提供相应域名的证书(domainx.com的证书),一个证书,你可以看做是一个包含了网站主体信息和站长的公私钥和CA的签名的一个文.由于现代操作系统中存储有许多CA的公钥,因此操作系统能够验证CA对网站证书的签名,而验证了CA对网站证书的签名,就等同于验证了网站证书的完整性在递送过程中没有受到影响(未受篡改),由于网站的证书还包含了站长的公钥,因此,浏览器可以拿这个公钥来验证证书上面声称的主体信息(例如证书声称这个证书对应的主体常用名称是domainx.com),可以看做是,拿站长的公钥,来验证站长对证书的签名.

信任链的图示

信任链的图示

首先用户相信自己的操作系统存储的钥匙环并没有被恶意篡改,然后浏览器会使用操作系统钥匙环中存储着的CA的公钥的散列值验证收到的证书中的CA公钥的完整性,证明了CA公钥的完整性之后,在用CA的公钥来验证CA的证书的完整性,其中,CA的证书也含有CA的主体信息和CA的公钥,在证明了CA的证书的完整性之后,再用CA的公钥来验证网站证书的完整性,因为网站证书有CA的签名,所以可用CA的公钥来验证CA在网站证书中的签名,然后,证明了网站证书的完整性之后,可以拿网站证书的站长公钥,来验证网站证书的完整性,从而保证网站证书中的网站主体信息没有被篡改,然后浏览器比较网站证书中的主体信息的「常用主体名称」字段和当前请求的域名是否匹配,如果匹配,则整个网站证书验证过程成功完成,即证明浏览器收到了的响应是来自合法的服务器.

由此,我们就引出了「信任链」的概念,验证也可以看成是一种信任,因为你相信验证结果具有意义,即,你「信任」这个验证得出的结.用户信任操作系统的钥匙环的完整性,操作系统的钥匙环信任CA不会给不合法的服务器签发要请求的网站的证书,CA(经过调查和服务器完成主体授权挑战)信任该服务器有资格提供相应的域名的内容从而签发证书,再结合验证过程,就实现了

用户 --信任--> 操作系统 并且

操心系统 --信任--> CA 并且

CA --信任--> 网站与服务器主体身份一致性

的这么样的一条信任链或者叫验证链.

信任链事实上也可以推广到更广泛的话题上,例如,消费者信任超市的食品的质量和安全性,超市信任供应商的食品的质量和安全性,供应商信任生产食品的农民.从而实现了消费者对食品安全的信.或者,就投资而言,客户信任基金经理,基金经理信任投资对象的经营者的经营能力,而投资对象的经营者信任市场前景,因而也就实现了客户对资金投资收益前景的信.

整个信任链条中最基本的受信任节点,被称作「信任锚」(trust anchor),在HTTPS验证涉及到的信任链中,CA运营主体的道德品质和守法程度被看做是信任锚,我们相信CA不会违背职业道德,给别的未经过足够主体身份信息核对的服务器也签发自己网站的证书,我们还相信CA会妥善保管自己的私钥使之不被泄露,因而我们相信CA,这就是CA作为「信任锚」的原.信任锚,就是信任最初产生的地方.

实际生产环境中,验证HTTPS证书所涉及到的信任链往往不止2层,可能有3层甚至4层(例如亚马逊AWS的网站),更加复杂的情况也有,比如说交叉信任链,但这不在我们的讨论范围内.

三层证书信任链

三层证书信任链

四层证书信任链

四层证书信任链

拥有四层证书信任链的证书一般是颁发给较为复杂且对灵活性和合规性要求较高的业务,最底层的根证书的私钥需要受到极其严密的保护,不过大多数情况,还是三层证书居多,这样可以保证最底层的根证书的私钥极少使用,而要签发网站证书,只要用到二级CA的私钥,这里,拿cloudflare.com举例,签发根证书的CA叫做DigiCert High Assurance EV Root CA,也叫根CA,而二级CA叫做DigiCert ECC Extended Validation Server CA,也叫二级CA或者服务器CA,一般来说根CA负责给二级CA的证书签名(从而大家得以验证二级CA的证书的完整性),而二级证书的CA给网站证书签名(从而大家得以验证网站证书的完整性),二级证书的CA的私钥会常被用到,而根CA的私钥则事实上受到了良好的保护,因为它极少会被访问到和使用到.

3层或者是4层的网站证书的验证,和最开始我们演示的:2层网站证书的验证,过程是类似的,浏览器借助操作系统的钥匙环验证根CA证书的完整性,再借助根CA证书验证2级CA证书的完整性,再借助2级CA证书验证网站证书的完整性,再借助网站证书的公钥验证网站证书表明的主体信息的完整性,以此来完成整个HTTPS证书链的验证.

和服务器建立双向的加密信道

我们知道,借助于一个主体的公钥,我们可以使用这个公钥向拥有这个公钥的主体发送加密信息,而发送的这些加密信息,只有公钥的所属主体才能用公钥对应的私钥来解密取得明.譬如说,David拥有Bob的公钥,那么David可以向Bob发送加密信息,而如果Bob有David的公钥,Bob也可以向David发送加密信息.

现在的问题是,浏览器根据HTTPS协议完成了对服务器发来的证书的完整性的检验,已经确保该证书未被篡改且该证书表明的主体名称正匹配得上浏览器正在访问的网站的域名,那么无疑浏览器可以发送加密信息给服务器,而服务器又没有浏览器的公钥,服务器怎么能够给浏览器发送加密信息呢?事实上是不能直接发送,但是通过一种叫Diffie-Hellman key exchange的方法,再基于浏览器能够向服务器发送加密信息的这一事实(尽管服务器一开始不能直接向浏览器发送加密信息),浏览器能够和服务器同时算出一个整数,这个整数只有浏览器和服务器知道,任何第三方都不知道,因而,被称作Shared Secret,即「共享的秘密」,再由这个「共享的秘密」,浏览器和服务器可以实现对称加密,即浏览器和服务器使用同一套密码来对要发送的消息和收到的消息进行加解密,以此实现浏览器和服务器双向的加密通信,而不再仅仅是单向的.

这么说可能不够具体,细说一下这个「共享秘密」是如何算出来的的也无妨.

原根

Definition (primitive root): $g$ is a primitive root modulo $n$ iff. for all $x$, where $x$ coprime to $n$, there exist a least an integer $k$ such that $g^k \equiv x (\text{mod } n)$.

也就是说如果$g$这个整数是$n$的原根的话,那必定得对每一个和$n$互质的数$x$,都能找到某个整数$k$使得$g^k$和$x$模$n$同.这时我们说:$g$是$n$的原根.

下面我们举一个例子,比如说,$3$这个数呢它是$7$的原根,怎么验证这一点呢?且看

$$ 3^1 \equiv 3 (\text{mod } 7) \\
3^2 \equiv 2 (\text{mod } 7) \\
3^3 \equiv 6 (\text{mod } 7) \\
3^4 \equiv 4 (\text{mod } 7) \\
3^5 \equiv 5 (\text{mod } 7) \\
3^6 \equiv 1 (\text{mod } 7) $$

也就是说为了验证$3$是$7$的原根,我们分别算了从$3$的$1$次方一直到$3$的$6$次方的值,然后再把每个数都对$7$取余数,比如说,$3$的$4$次方除以$7$,余数是$4$,所以呢,$3$的$4$次方和$4$这两个数是模$7$同余的,同样地,$3$的$6$次方除以$7$,余数呢是$1$,所以说,$3$的$6$次方和$1$这两个数之间有这么一种叫做模$7$同余的关系.

我们为什么要这么算呢?为什么要把$3$的$1$次方一直到$3$的$6$次方统统算一遍然后再分别对$7$取余数呢?我们且看同余符号$\equiv$的右边,你可能已经注意到了,同余符号右边的数字,实际上包括了$1$到$6$这个范围的每一个数,同余号右边有$3,2,6,4,5,1$,可以看到$1$到$6$这个范围的每一个数都在里面.我们再回顾一下原根的定义,定义说要对「每一个」和$n$互质,这里$n$是$7$,每一个和$7$互质的数$x$都要能找到一个正整数$k$使$g$,这里$g$是$3$,使$g^k \equiv x (\text{mod } n)$成立,注意到这么一个事实,如果一个数$x$它和$7$互质,那么这个数$x$对$7$取得的余数必定不是$0$,也就是实际上就有$x \equiv a (\text{mod } n), a=1,2,3,4,5,6$,这种关系,现在呢,对上面那一排式子我们改写一下:

$$ 3^1 \equiv 3 \equiv x_3 (\text{mod } 7) \\
3^2 \equiv 2 \equiv x_2 (\text{mod } 7) \\
3^3 \equiv 6 \equiv x_6 (\text{mod } 7) \\
3^4 \equiv 4 \equiv x_4 (\text{mod } 7) \\
3^5 \equiv 5 \equiv x_5 (\text{mod } 7) \\
3^6 \equiv 1 \equiv x_1 (\text{mod } 7) $$

其中的$x_i$表示所有和$7$互质的数当中余数是$i$的,所以呢,$x_1,x_2,\cdots,x_6$实质上就足以表示所有的和$7$互质的数了(模$7$剩余类.任何一个和$7$互质的数,它必定会是$x_1,\cdots,x_6$之中的某一个,而对$x_1,\cdots,x_6$我们再看同余号左边,实质上都是$g^k$的这种形式,都能找到这样的正整数$k$使同余关系式成立,从而我们就证明了$3$这个数它是$7$的这么样的一个原.证明完成啦.

下面我们再举一个例子,看为什么$4$这个数并不是$7$这个数它的一个原.还是用的是差不多的验证方法,且看

$$ 4^1 \equiv 4 (\text{mod } 7) \\
4^2 \equiv 2 (\text{mod } 7) \\
4^3 \equiv 1 (\text{mod } 7) \\
4^4 \equiv 4 (\text{mod } 7) \\
4^5 \equiv 2 (\text{mod } 7) \\
4^6 \equiv 1 (\text{mod } 7) \\
4^7 \equiv 4 (\text{mod } 7) \\
4^8 \equiv 2 (\text{mod } 7) \\
4^9 \equiv 1 (\text{mod } 7) \\
4^{10} \equiv 4 (\text{mod } 7) \\
4^{11} \equiv 2 (\text{mod } 7) \\
$$

你能看到右边是重复的,我多算几步是为了提示大家最多只算到$6$次方就够了,这里其实算到$4$次方就已经能看到循环了,就可以不用再往下算了,因为右边的余数它是会循环的,循环的周期最大是$n-1$,这里$n$是$7$,所以循环周期就是$7-1$就是$6$.

我们从上面的式子组可以看到,对于$x_3,x_5,x_6$,也就是对于所有和$7$互质的数当中,余数为$3$或者$5$或者$6$的哪些,都不会有$k$使得$4^k \equiv x_3 (\text{mod } n)$,$4^k \equiv x_4 (\text{mod } n)$,或者是$4^k \equiv x_6 (\text{mod } n)$,因为左边的那个$k$它再怎么加,右边的余数也是循环出现的那几个,不会有$3$,$5$或者$6$,所以,根据定义,$4$这个数它不是$7$这个数的原根.

讲到这里希望您已经对「原根」(primitive root)这个概念有了一个最基本的了.接下来可以开始讲解迪菲-赫尔曼密钥交换过程了.

Diffie–Hellman key exchange

迪菲赫曼密钥交换方法可以用于在可确保接收方身份的公开信道上,通过一系列模$p$剩余类环上的乘法运算,使参与双方最终都计算出同一个「共享秘密」(shared secret).

首先一般来说参与双方会选择一个比较大的质数$p$,和这个质数$p$的一个原根$g$,这个讨论可以在公开信道上进行,在启用了HTTPS的情况下浏览器干脆可以把$p$和$g$加密发送给服务.在这里作为演示我们只选择了$p=29$,也就是第10个质数,而$g$可以不用取太大,这里取$g=2$也没关系,是原根就行.

我们假设参与的其中一方叫做Alice,另一方叫做Bob,然后Alice首先挑选一个自己知道的秘密,可以是随机挑选的,比较大的数,$a$,也就是说这个秘密数$a$只有Alice知道,这里用作演示我们选了$a=32$,然后Alice计算要在公开信道上发送给Bob的$A$,$A$等于

$$ A = g^a \; \text{mod} \; p $$

也就是$g$的$a$次方对$p$取余数,这里我们算得$A=16$,然后发送给Bo.

与此同时,Bob也会选择自己的秘密数,这里Bob选择了$b=219$,同样的,$b$实际上也应该选择大一些的数为了安全,但是作为演示,我们只选了219,然后同样的Bob也计算要在公开信道上发送给Alice的数$B=g^b \; \text{mod} \; p$,Bob算得$B=10$,然后Bob把$10$发送给Alice.

Alice在收到$B=10$这个讯息之后,Alice会计算$s=B^a \; \text{mod} \; p$,Alice算得$s=24.Bob在收到$A=16$之后,类似地计算$s=A^b \; \text{mod} \; p$,Bob刚好也算得$s=24$,但这其实不是巧.其中的$a$和$b$也就是刚才Alice和Bob自己挑选的秘密数.

在这期间,中间人Mallory可能会知道$p=29, g=2, A=16, B=10$,但是Mallory不知道$a=32, b=219, s=24$,而最终Alice和Bob都算出了$s=24$,于是之后他们得以用$s=24$这个只有他俩知道的共享秘密来构建一个双向对称加密信道.

整个计算过程我自己已经用Mathematica软件实验了一遍,实验过程和结果如下图所示:

迪菲赫曼密钥交换方法计算过程演示

迪菲赫曼密钥交换方法计算过程演示

下表中的每一行对应每一个DHKE方法中的每一个过程或者说每一个阶段,各列列出的是参与方(也包括中间人Mallory)知道的和不知道的信息.

信息表关于参与方知道的和不知道的

信息表关于参与方知道的和不知道的

值得注意的是,参与方必须要确认对方是对方,Alice在收到信息之后,必须要确定那是Bob发来的,同样Bob在收到信息之后,也必须要确定那是Alice发来的,否则应当终止DHKE,因为在不能够确认对方身份的情况下,有可能是Mallory对Alice伪装成Bob,同时Mallory也对Bob伪装成Alice,这样DHKE方法计算出来的$A, B$和$s$实际上也会被Mallory截取,这叫做「中间人攻击」,DHKE方法面临中间人攻击时会变得不再安全.

通过上面的讲解呢,希望您已经了解通过DHKE方法在公开信道上构建共享秘密的基本过程和DHKE的基本概念.

对称双向加密信道

在通过DHKE方法计算出共享秘密之后,浏览器和服务器会根据一个同一个算法,计算出一个主密钥(Masterkey),然后用这个主密钥同时对要发送的信息进行加密和接收到的信息进行解密,这种加密方法实际上叫做对称加密,实现可以参看AES, RC4, Blowfish等,但实际上不止这些,具体使用哪种对称加密方法,也是通过HTTPS协议进行协商.

在这之后,浏览器和服务器就可以进行双向的加密通信了,对于之后的通信过程,浏览器和服务器解密数据后,就按照HTTP协议来处理解密得到的数.实际上浏览器也可以通过HTTPS证书上的公钥加密地给服务器发送自己的公钥,这样双方都有对方的公钥也能实现双向加密通信,但是公钥加密算法和解密算法所消耗的计算资源一般来说比对称加密的要多得多,加解密速度也会相对慢一些,所以非对称加密仅仅是在前期用于身份认证和构建对称双向加密信道,而一般不用于实际要传输的数据的加解密.

申请证书并检视证书

在真正给自己的网站启用HTTPS之前,我们知道,首先需要有一个由CA签发的HTTPS证.现在,对小型的个人博客网站和非商业组织的小型网站而言,大多是申请使用由Let’s Encrypt签发的免费证书,申请成功后会得到由Let’s Encrypt签发的HTTPS证书,过程其实非常简单,配合某些工具,还能够实现证书到期后自动续签(从而自动延长到期时间.当然也有其他方式获取证书,我们会逐一介绍.

Let’s Encrypt

申请Let’s Encrypt免费证书呢常用两种工具,一种叫acme.sh,是一段脚本程序,下载到服务器上执行,而另一种叫certbot,可以由包管理系统比如说apt进行安装,然后运行certbot就可以申请证书,这两种工具的原理也非常简单,无非两点:

  1. 假如说要申请的证书的主体名称是domainx.com,那程序会检查domainx.com这个域名是否真的指向当前服务器,比如说程序运行在104.17.176.85这台服务器上,而这台服务器正在申请domainx.com的证书,那程序会检查domainx.com是否真的指向104.17.176.85,这其实是ACME协议的一部.还有一种方式不一定是验证服务器是否匹配域名,而是验证站长对domainx.com的所有权,怎么验证?比如说要求站长在域名domianx.com上设置某些DNS记.总之是验证「正在执行申请domainx.com证书的人有管理domainx.com域名的DNS记录的权限或者domainx.com所指向的服务器的权限」,概括来说即「网站管理权」的验证.
  2. 向Let’s Encrypt发送网站管理权的验证结果和要申请的证书的主体信息(CSR文件),Let’s Encrypt签发后,把签发了的证书发送回来.

要用acme.sh这个工具申请Let’s Encrypt证书首先要下载相应的脚本并安装:

git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install

运行了上述命令之后,如果安装成功了,再运行

acme.sh version

应该能打印出当前已经安装的acme.sh的版本号:

https://github.com/acmesh-official/acme.sh
v2.8.6

接下来我要在哪台服务器申请证书,我就把要申请的证书的域名的唯一一个A记录指向哪台服务器,比如说我是在172.217.5.78这台服务器申请acme-demo.beyondstars.xyz这个域名的TLS证书,那我就在DNS解析商那里把这个acme-demo.beyondstars.xyz这个域名的唯一的一条A记录指向服务器172.217.5.78,这里呢172.217.5.78这个地址是用来举例,你要看你自己的VPS服务器也就是你运行acme.sh程序的那台服务器的IP地址是多少.

创建一个A记录指向运行acme.sh申请证书的服务器

创建一个A记录指向运行acme.sh申请证书的服务器

大概过几分钟,我们在自己机器上ping自己设置的这个域名,以我设置的举例,acme-demo.beyondstars.xyz,如果看到IP地址是刚才自己设置的,那就算是DNS解析设置成功了,就可以进入下一步了.

首先我们登陆VPS服务器,假如刚才你在DNS解析商那里把yoursite.com指向x.x.x.x这台服务器,那你就登陆x.x.x.x

ssh [email protected]

然后创建网站根目录,假如说你的网站在没有开通HTTPS之前就已经运作了,那应该是已经有网站根目录了,我们假设网站根目录是位于

/var/www/yoursite.com

如果还没有网站根目录,也没有网站,那我们以简单易于配置的Nginx服务器为例,先创建一个网站根目录

mkdir -p /var/www/yoursite.com

再在Nginx配置目录下添加新创建的网站

vi /etc/nginx/conf.d/yoursite.com.conf

在打开的这个文件中,填入以下内容

server {
    listen 80;
    server_name yoursite.com;
    root /var/www/yoursite.com;
}

保存并退出vi编辑器后发信号给nginx进程让其重新加载配置

nginx -s reload

如果一切正常,应该不会看到报错信.现在yoursite.com网站应该已经启用成功了,只不过还没有HTTPS证书而.下面开始申请证书,用acme.sh这个工具

acme.sh --issue \
--domain yoursite.com \
--webroot /var/www/yoursite.com 

申请成功后会看到下列提示,主要是提示申请到的证书的位置是在哪儿.

acme.sh成功申请到证书给出的提示

acme.sh成功申请到证书给出的提示

要安装acme.sh申请到的证书从而立刻给网站启用HTTPS服务也很简单,acme.sh要求我们把证书复制到一个单独的目录,而不建议直接使用$HOME/.acme作为证书目.首先我们新建一个这样的目录:

mkdir -p /etc/nginx/certs.d/yoursite.com

然后开始复制证书,用的是acme.sh提供的命令

acme.sh --install-cert \
--domain yoursite.com \
--key-file /etc/nginx/certs.d/yoursite.com/key.pem \
--fullchain-file /etc/nginx/certs.d/yoursite.com/fullchain.pem

复制成功后运行

ls -all /etc/nginx/certs.d/yoursite.com

应该会看到列出的key.pem(私钥)和fullchaim.pem(证书),然后我们在nginx中手动启用HTTPS,因为acme.sh不会像certbot那样帮我们配置`nginx.首先打开配置文件

vi /etc/nginx/conf.d/yoursite.com.conf

然后在最后边加入以下内容

server {
    listen 443 ssl;
    server_name yoursite.com;
    root /var/www/yoursite.com;
    ssl_certificate /etc/nginx/certs.d/yoursite.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs.d/yoursite.com/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
}

保存退出之后,执行

nginx -s reload

使nginx重新加载配置文件,之后可以测试看HTTPS是否已经启用

echo "this is a https-enabled site!" > /var/www/yoursite.com/https-test.html
curl https://yoursite.com/https-test.html

注意,为了验证HTTPS,上边的https一定不能是http,如果一切正常,应该能够看到输出

this is a https-enabled site!

这就说明您已经成功地通过acme.sh为您的网站启用HTTPS了.

如果仅仅是想查看客户端能否和网站成功地进行TLS握手,以acme-demo.beyondstars.xyz为例

echo "" | 
openssl s_client -brief \
-connect acme-demo.beyondstars.xyz:443 \
-verify_hostname acme-demo.beyondstars.xyz

会看到输出

CONNECTION ESTABLISHED
Protocol version: TLSv1.2
Ciphersuite: ECDHE-RSA-AES256-GCM-SHA384
Peer certificate: CN = acme-demo.beyondstars.xyz
Hash used: SHA256
Signature type: RSA-PSS
Verification: OK
Verified peername: acme-demo.beyondstars.xyz
Supported Elliptic Curve Point Formats: uncompressed:ansiX962_compressed_prime:ansiX962_compressed_char2
Server Temp Key: X25519, 253 bits
DONE

会看到Verification: OK,这样就验证了TLS握手成功了,当前主机查询到的acme-demo.beyondstars.xyz的IP地址对应的服务器能够拿得出合法的acme-demo.beyondstars.xyz的TLS证书来证明自身提供acme-demo.beyondstars.xyz的内容是经过授权的.

否则我们可以尝试向另外一个主机请求acme-demo.beyondstars.xyz这个网站,看TLS握手的证书验证过程是否会通过:

echo "" | 
openssl s_client -brief \
-connect 8.8.8.8:443 \
-servername acme-demo.beyondstars.xyz \
-verify_hostname acme-demo.beyondstars.xyz

会看到输出

depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = dns.google
verify error:num=62:Hostname mismatch
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: C = US, ST = California, L = Mountain View, O = Google LLC, CN = dns.google
Hash used: SHA256
Signature type: RSA-PSS
Verification error: Hostname mismatch
Server Temp Key: X25519, 253 bits
DONE

注意到其中的Hostname mismatch即表明连接到的服务器无法拿出有效证书完成验证,如果一般情况下发生这样子的情况,可能是DNS污染或者中间人攻击导致的.

我们还可以使用openssl命令和sed命令下载相应网站的TLS证书以供更详细的分析,首先,最简单地,使用openssl s_client工具查看TLS握手情况:

运行的是这个命令

echo "" | 
openssl s_client \
-connect acme-demo.beyondstars.xyz:443 \
-verify_hostname acme-demo.beyondstars.xyz

会看到目标网站的一些信息,比如证书的签发者,证书的主体名称,和验证结果等,还有证书内容,还有证书的签发者(CA)的主体名称,但是这里没有打印出签发者的证书内容

CONNECTED(00000005)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = acme-demo.beyondstars.xyz
verify return:1
---
Certificate chain
 0 s:CN = acme-demo.beyondstars.xyz
   i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
 1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFajCCBFKgAwIBAgISA0s+9XUzvUX+yEbwT3IQhb7DMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA0MDcwMjIxNTRaFw0y
MDA3MDYwMjIxNTRaMCQxIjAgBgNVBAMTGWFjbWUtZGVtby5iZXlvbmRzdGFycy54
eXowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiS+Q+Ko3o/UtanTOk
SsKGIVbTn6g0MEyqeZcyJGittYSgxGetIerOpM/PUCK7q01xoiIL5BDB9ntge0mx
TVI3uE4oHj3gS8bc01IHfSsXFQ9fiJsHyH9/pTG5aeFCsKY1JzccG/I/eaJg1s+S
D9xsht0alTzOpeMcgDvN0Ah0RJbzgLASUSy82yEuDt5IKD2JzhS/kxWJs4CnKA0M
EtlhlWrG5+6ZDIX8gWaenoiGFWGK6OR2iL1vo7Yw3O2nqVRCfD59XCV6O0C5hgcL
JG9vk3uKfG9hKI7zlHqXV+vrUWtE/kSFb77YQqcwR78AC5Aixv5IVFwQ1DgGyyey
RUvHAgMBAAGjggJuMIICajAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFNaYRd97HS/m
dE+Aukzr3cwkCouEMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8G
CCsGAQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxl
dHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxl
dHNlbmNyeXB0Lm9yZy8wJAYDVR0RBB0wG4IZYWNtZS1kZW1vLmJleW9uZHN0YXJz
Lnh5ejBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsG
AQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQQGCisGAQQB1nkC
BAIEgfUEgfIA8AB2APCVpFnyANGCQBAtL5OIjq1L/h1H45nh0DSmsKiqjrJzAAAB
cVKooAgAAAQDAEcwRQIgb/6rjUn6mv2Zqn0UwCvjyLfPynPkY+nc3oK6iNeveDYC
IQDUXJXDA7Q3gUY5qaXbbuE76bgOJknoPzIDKYXeI2SZDAB2AAe3XBvlfWj/8bDG
HSMVx7rmV3xXlLdq7rxhOhpp06IcAAABcVKooFQAAAQDAEcwRQIhAJKJy5MpC8TQ
CoGRd+MTsn9AD+j4jFwgZiBhW079rPFcAiAwSJUS6zZzDX8poRJXxcfy120rc5v4
gF3ZbXOnXMZ/2zANBgkqhkiG9w0BAQsFAAOCAQEABDTN/Af/e5sx4lkd1Vm9yw0L
dSe6pbgRHK7bdhj7QKdl5/ExsyyM1mOdJdZwVQRJ2PpWnbyCOHrH4cpvN8Un8z8z
r4ta35QGvXi+LpC98FD0vfr3xCLfHXdi2Uh44ukKfoIGONpSi9F6O2ibRDBuyJ48
1lCIUvJSHF7Pz1dV4gGOQx8w6Jkha7IU6oXa5GnBI23yjmB/Jj+V9rq/oj/mS9mz
TYgUtut6go4yai4BEWzgvlf3Lzex6WBQYhDYSIxFOZbrlPRKN5AHb1WtVaQUpix0
DrkKWJ/HkiwkqwDD+962bG62zox2tG+p7N9lWFEdyT0jglC4a57hR0g9tUhPNA==
-----END CERTIFICATE-----
subject=CN = acme-demo.beyondstars.xyz

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3244 bytes and written 420 bytes
Verification: OK
Verified peername: acme-demo.beyondstars.xyz
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 3818012F5B90D6CF3615028C0AF679DF5552914DED8904180E58F2C904CE32E7
    Session-ID-ctx:
    Master-Key: 0353E57EAA12C4E91D0D1D88444B608A9DB58D73AE4C79D2DB0CE98498241218BC5E997C880DD88F2AC66EE1C84CFE2F
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 64 29 1e 78 29 d5 59 ba-72 1b e8 6d 1a bc 40 10   d).x)[email protected]
    0010 - cc e9 bd 2e 06 3f 41 6d-57 70 36 26 48 10 e6 5e   .....?AmWp6&H..^
    0020 - de 93 8d 63 d0 38 33 17-78 11 12 89 5d 19 cf 59   ...c.83.x...]..Y
    0030 - 39 05 25 15 0c 8b 4f bf-f3 b8 12 28 fb e2 00 97   9.%...O....(....
    0040 - 6b b6 10 0c a4 1b 67 d5-68 6e a7 14 69 dd 00 cf   k.....g.hn..i...
    0050 - e4 42 06 86 da 2e 23 fc-7f be 51 b0 c9 4d 1d d9   .B....#...Q..M..
    0060 - 3b f5 e6 13 d5 4c 08 07-6a fc 49 28 1a 00 02 ff   ;....L..j.I(....
    0070 - 94 92 46 f0 ed 9f d2 a8-43 30 87 cb 17 4c 86 79   ..F.....C0...L.y
    0080 - 0e 5e c3 50 33 0d 7a a7-64 1e 29 98 7b 9a 5d f4   .^.P3.z.d.).{.].
    0090 - 28 61 6c e8 8e fd 92 27-d2 0c c7 61 87 54 ae 94   (al....'...a.T..
    00a0 - 8d 51 7b a7 84 18 1b 0e-bf fc 92 e0 f7 53 93 8c   .Q{..........S..
    00b0 - 29 ce c9 d1 9a 1a 8e d4-08 10 f8 bc b7 9b 0a 65   )..............e
    00c0 - 9d e8 e2 72 0a 46 bb af-0e 6a 97 d2 cb 38 32 e9   ...r.F...j...82.

    Start Time: 1586244626
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---
DONE

上面这段信息字可能比较多,我们也可以只看证书的一些信息,用这个命令

echo "" | 
openssl s_client \
-connect acme-demo.beyondstars.xyz:443 \
-verify_hostname acme-demo.beyondstars.xyz | 
openssl x509 -noout -text

输出结果相比前面的更加详细地显示了证书的信息

depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = acme-demo.beyondstars.xyz
verify return:1
Certificate:
DONE
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:4b:3e:f5:75:33:bd:45:fe:c8:46:f0:4f:72:10:85:be:c3
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
        Validity
            Not Before: Apr  7 02:21:54 2020 GMT
            Not After : Jul  6 02:21:54 2020 GMT
        Subject: CN = acme-demo.beyondstars.xyz
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:e2:4b:e4:3e:2a:8d:e8:fd:4b:5a:9d:33:a4:4a:
                    c2:86:21:56:d3:9f:a8:34:30:4c:aa:79:97:32:24:
                    68:ad:b5:84:a0:c4:67:ad:21:ea:ce:a4:cf:cf:50:
                    22:bb:ab:4d:71:a2:22:0b:e4:10:c1:f6:7b:60:7b:
                    49:b1:4d:52:37:b8:4e:28:1e:3d:e0:4b:c6:dc:d3:
                    52:07:7d:2b:17:15:0f:5f:88:9b:07:c8:7f:7f:a5:
                    31:b9:69:e1:42:b0:a6:35:27:37:1c:1b:f2:3f:79:
                    a2:60:d6:cf:92:0f:dc:6c:86:dd:1a:95:3c:ce:a5:
                    e3:1c:80:3b:cd:d0:08:74:44:96:f3:80:b0:12:51:
                    2c:bc:db:21:2e:0e:de:48:28:3d:89:ce:14:bf:93:
                    15:89:b3:80:a7:28:0d:0c:12:d9:61:95:6a:c6:e7:
                    ee:99:0c:85:fc:81:66:9e:9e:88:86:15:61:8a:e8:
                    e4:76:88:bd:6f:a3:b6:30:dc:ed:a7:a9:54:42:7c:
                    3e:7d:5c:25:7a:3b:40:b9:86:07:0b:24:6f:6f:93:
                    7b:8a:7c:6f:61:28:8e:f3:94:7a:97:57:eb:eb:51:
                    6b:44:fe:44:85:6f:be:d8:42:a7:30:47:bf:00:0b:
                    90:22:c6:fe:48:54:5c:10:d4:38:06:cb:27:b2:45:
                    4b:c7
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                D6:98:45:DF:7B:1D:2F:E6:74:4F:80:BA:4C:EB:DD:CC:24:0A:8B:84
            X509v3 Authority Key Identifier:
                keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1

            Authority Information Access:
                OCSP - URI:http://ocsp.int-x3.letsencrypt.org
                CA Issuers - URI:http://cert.int-x3.letsencrypt.org/

            X509v3 Subject Alternative Name:
                DNS:acme-demo.beyondstars.xyz
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.44947.1.1.1
                  CPS: http://cps.letsencrypt.org

            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : F0:95:A4:59:F2:00:D1:82:40:10:2D:2F:93:88:8E:AD:
                                4B:FE:1D:47:E3:99:E1:D0:34:A6:B0:A8:AA:8E:B2:73
                    Timestamp : Apr  7 03:21:54.952 2020 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:20:6F:FE:AB:8D:49:FA:9A:FD:99:AA:7D:14:
                                C0:2B:E3:C8:B7:CF:CA:73:E4:63:E9:DC:DE:82:BA:88:
                                D7:AF:78:36:02:21:00:D4:5C:95:C3:03:B4:37:81:46:
                                39:A9:A5:DB:6E:E1:3B:E9:B8:0E:26:49:E8:3F:32:03:
                                29:85:DE:23:64:99:0C
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 07:B7:5C:1B:E5:7D:68:FF:F1:B0:C6:1D:23:15:C7:BA:
                                E6:57:7C:57:94:B7:6A:EE:BC:61:3A:1A:69:D3:A2:1C
                    Timestamp : Apr  7 03:21:55.028 2020 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:92:89:CB:93:29:0B:C4:D0:0A:81:91:
                                77:E3:13:B2:7F:40:0F:E8:F8:8C:5C:20:66:20:61:5B:
                                4E:FD:AC:F1:5C:02:20:30:48:95:12:EB:36:73:0D:7F:
                                29:A1:12:57:C5:C7:F2:D7:6D:2B:73:9B:F8:80:5D:D9:
                                6D:73:A7:5C:C6:7F:DB
    Signature Algorithm: sha256WithRSAEncryption
         04:34:cd:fc:07:ff:7b:9b:31:e2:59:1d:d5:59:bd:cb:0d:0b:
         75:27:ba:a5:b8:11:1c:ae:db:76:18:fb:40:a7:65:e7:f1:31:
         b3:2c:8c:d6:63:9d:25:d6:70:55:04:49:d8:fa:56:9d:bc:82:
         38:7a:c7:e1:ca:6f:37:c5:27:f3:3f:33:af:8b:5a:df:94:06:
         bd:78:be:2e:90:bd:f0:50:f4:bd:fa:f7:c4:22:df:1d:77:62:
         d9:48:78:e2:e9:0a:7e:82:06:38:da:52:8b:d1:7a:3b:68:9b:
         44:30:6e:c8:9e:3c:d6:50:88:52:f2:52:1c:5e:cf:cf:57:55:
         e2:01:8e:43:1f:30:e8:99:21:6b:b2:14:ea:85:da:e4:69:c1:
         23:6d:f2:8e:60:7f:26:3f:95:f6:ba:bf:a2:3f:e6:4b:d9:b3:
         4d:88:14:b6:eb:7a:82:8e:32:6a:2e:01:11:6c:e0:be:57:f7:
         2f:37:b1:e9:60:50:62:10:d8:48:8c:45:39:96:eb:94:f4:4a:
         37:90:07:6f:55:ad:55:a4:14:a6:2c:74:0e:b9:0a:58:9f:c7:
         92:2c:24:ab:00:c3:fb:de:b6:6c:6e:b6:ce:8c:76:b4:6f:a9:
         ec:df:65:58:51:1d:c9:3d:23:82:50:b8:6b:9e:e1:47:48:3d:
         b5:48:4f:34

其中前面几行显示的是证书链:

depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = acme-demo.beyondstars.xyz
verify return:1

然后可以看到证书有效期:

Not Before: Apr  7 02:21:54 2020 GMT
Not After : Jul  6 02:21:54 2020 GMT

我们还可以把网址的证书下载到本地

echo "" | 
openssl s_client \
-connect acme-demo.beyondstars.xyz:443 \
-verify_hostname acme-demo.beyondstars.xyz \
-showcerts | 
sed \
-n "/-\+BEGIN CERTIFICATE-\+/,/-\+END CERTIFICATE-\+/p" \
> fullchain.pem

这样当前文件夹下的fullchain.pem就同时包含了中间CA的证书和网站的证书,我们可以用csplit工具将fullchain.pem分解为网站TLS证书和中间CA的TLS证书:

csplit --prefix="cert" --suffix="%02d.pem" fullchain.pem '/-----BEGIN CERTIFICATE-----/' '{*}'

会得到

cert00.pem
cert01.pem
cert02.pem

其中cert00.pem是空白.然后可以分别查看cert01.pemcert02.pem,先查看cert01.pem

openssl x509 -noout -text -in cert01.pem

显示如下

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:4b:3e:f5:75:33:bd:45:fe:c8:46:f0:4f:72:10:85:be:c3
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
        Validity
            Not Before: Apr  7 02:21:54 2020 GMT
            Not After : Jul  6 02:21:54 2020 GMT
        Subject: CN = acme-demo.beyondstars.xyz
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:e2:4b:e4:3e:2a:8d:e8:fd:4b:5a:9d:33:a4:4a:
                    c2:86:21:56:d3:9f:a8:34:30:4c:aa:79:97:32:24:
                    68:ad:b5:84:a0:c4:67:ad:21:ea:ce:a4:cf:cf:50:
                    22:bb:ab:4d:71:a2:22:0b:e4:10:c1:f6:7b:60:7b:
                    49:b1:4d:52:37:b8:4e:28:1e:3d:e0:4b:c6:dc:d3:
                    52:07:7d:2b:17:15:0f:5f:88:9b:07:c8:7f:7f:a5:
                    31:b9:69:e1:42:b0:a6:35:27:37:1c:1b:f2:3f:79:
                    a2:60:d6:cf:92:0f:dc:6c:86:dd:1a:95:3c:ce:a5:
                    e3:1c:80:3b:cd:d0:08:74:44:96:f3:80:b0:12:51:
                    2c:bc:db:21:2e:0e:de:48:28:3d:89:ce:14:bf:93:
                    15:89:b3:80:a7:28:0d:0c:12:d9:61:95:6a:c6:e7:
                    ee:99:0c:85:fc:81:66:9e:9e:88:86:15:61:8a:e8:
                    e4:76:88:bd:6f:a3:b6:30:dc:ed:a7:a9:54:42:7c:
                    3e:7d:5c:25:7a:3b:40:b9:86:07:0b:24:6f:6f:93:
                    7b:8a:7c:6f:61:28:8e:f3:94:7a:97:57:eb:eb:51:
                    6b:44:fe:44:85:6f:be:d8:42:a7:30:47:bf:00:0b:
                    90:22:c6:fe:48:54:5c:10:d4:38:06:cb:27:b2:45:
                    4b:c7
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                D6:98:45:DF:7B:1D:2F:E6:74:4F:80:BA:4C:EB:DD:CC:24:0A:8B:84
            X509v3 Authority Key Identifier:
                keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1

            Authority Information Access:
                OCSP - URI:http://ocsp.int-x3.letsencrypt.org
                CA Issuers - URI:http://cert.int-x3.letsencrypt.org/

            X509v3 Subject Alternative Name:
                DNS:acme-demo.beyondstars.xyz
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.44947.1.1.1
                  CPS: http://cps.letsencrypt.org

            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : F0:95:A4:59:F2:00:D1:82:40:10:2D:2F:93:88:8E:AD:
                                4B:FE:1D:47:E3:99:E1:D0:34:A6:B0:A8:AA:8E:B2:73
                    Timestamp : Apr  7 03:21:54.952 2020 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:20:6F:FE:AB:8D:49:FA:9A:FD:99:AA:7D:14:
                                C0:2B:E3:C8:B7:CF:CA:73:E4:63:E9:DC:DE:82:BA:88:
                                D7:AF:78:36:02:21:00:D4:5C:95:C3:03:B4:37:81:46:
                                39:A9:A5:DB:6E:E1:3B:E9:B8:0E:26:49:E8:3F:32:03:
                                29:85:DE:23:64:99:0C
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 07:B7:5C:1B:E5:7D:68:FF:F1:B0:C6:1D:23:15:C7:BA:
                                E6:57:7C:57:94:B7:6A:EE:BC:61:3A:1A:69:D3:A2:1C
                    Timestamp : Apr  7 03:21:55.028 2020 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:92:89:CB:93:29:0B:C4:D0:0A:81:91:
                                77:E3:13:B2:7F:40:0F:E8:F8:8C:5C:20:66:20:61:5B:
                                4E:FD:AC:F1:5C:02:20:30:48:95:12:EB:36:73:0D:7F:
                                29:A1:12:57:C5:C7:F2:D7:6D:2B:73:9B:F8:80:5D:D9:
                                6D:73:A7:5C:C6:7F:DB
    Signature Algorithm: sha256WithRSAEncryption
         04:34:cd:fc:07:ff:7b:9b:31:e2:59:1d:d5:59:bd:cb:0d:0b:
         75:27:ba:a5:b8:11:1c:ae:db:76:18:fb:40:a7:65:e7:f1:31:
         b3:2c:8c:d6:63:9d:25:d6:70:55:04:49:d8:fa:56:9d:bc:82:
         38:7a:c7:e1:ca:6f:37:c5:27:f3:3f:33:af:8b:5a:df:94:06:
         bd:78:be:2e:90:bd:f0:50:f4:bd:fa:f7:c4:22:df:1d:77:62:
         d9:48:78:e2:e9:0a:7e:82:06:38:da:52:8b:d1:7a:3b:68:9b:
         44:30:6e:c8:9e:3c:d6:50:88:52:f2:52:1c:5e:cf:cf:57:55:
         e2:01:8e:43:1f:30:e8:99:21:6b:b2:14:ea:85:da:e4:69:c1:
         23:6d:f2:8e:60:7f:26:3f:95:f6:ba:bf:a2:3f:e6:4b:d9:b3:
         4d:88:14:b6:eb:7a:82:8e:32:6a:2e:01:11:6c:e0:be:57:f7:
         2f:37:b1:e9:60:50:62:10:d8:48:8c:45:39:96:eb:94:f4:4a:
         37:90:07:6f:55:ad:55:a4:14:a6:2c:74:0e:b9:0a:58:9f:c7:
         92:2c:24:ab:00:c3:fb:de:b6:6c:6e:b6:ce:8c:76:b4:6f:a9:
         ec:df:65:58:51:1d:c9:3d:23:82:50:b8:6b:9e:e1:47:48:3d:
         b5:48:4f:34

再来看cert02.pem

openssl x509 -noout -text -in cert02.pem

显示如下

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            0a:01:41:42:00:00:01:53:85:73:6a:0b:85:ec:a7:08
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: O = Digital Signature Trust Co., CN = DST Root CA X3
        Validity
            Not Before: Mar 17 16:40:46 2016 GMT
            Not After : Mar 17 16:40:46 2021 GMT
        Subject: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:9c:d3:0c:f0:5a:e5:2e:47:b7:72:5d:37:83:b3:
                    68:63:30:ea:d7:35:26:19:25:e1:bd:be:35:f1:70:
                    92:2f:b7:b8:4b:41:05:ab:a9:9e:35:08:58:ec:b1:
                    2a:c4:68:87:0b:a3:e3:75:e4:e6:f3:a7:62:71:ba:
                    79:81:60:1f:d7:91:9a:9f:f3:d0:78:67:71:c8:69:
                    0e:95:91:cf:fe:e6:99:e9:60:3c:48:cc:7e:ca:4d:
                    77:12:24:9d:47:1b:5a:eb:b9:ec:1e:37:00:1c:9c:
                    ac:7b:a7:05:ea:ce:4a:eb:bd:41:e5:36:98:b9:cb:
                    fd:6d:3c:96:68:df:23:2a:42:90:0c:86:74:67:c8:
                    7f:a5:9a:b8:52:61:14:13:3f:65:e9:82:87:cb:db:
                    fa:0e:56:f6:86:89:f3:85:3f:97:86:af:b0:dc:1a:
                    ef:6b:0d:95:16:7d:c4:2b:a0:65:b2:99:04:36:75:
                    80:6b:ac:4a:f3:1b:90:49:78:2f:a2:96:4f:2a:20:
                    25:29:04:c6:74:c0:d0:31:cd:8f:31:38:95:16:ba:
                    a8:33:b8:43:f1:b1:1f:c3:30:7f:a2:79:31:13:3d:
                    2d:36:f8:e3:fc:f2:33:6a:b9:39:31:c5:af:c4:8d:
                    0d:1d:64:16:33:aa:fa:84:29:b6:d4:0b:c0:d8:7d:
                    c3:93
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
            Authority Information Access:
                OCSP - URI:http://isrg.trustid.ocsp.identrust.com
                CA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c

            X509v3 Authority Key Identifier:
                keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10

            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.44947.1.1.1
                  CPS: http://cps.root-x1.letsencrypt.org

            X509v3 CRL Distribution Points:

                Full Name:
                  URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl

            X509v3 Subject Key Identifier:
                A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1
    Signature Algorithm: sha256WithRSAEncryption
         dd:33:d7:11:f3:63:58:38:dd:18:15:fb:09:55:be:76:56:b9:
         70:48:a5:69:47:27:7b:c2:24:08:92:f1:5a:1f:4a:12:29:37:
         24:74:51:1c:62:68:b8:cd:95:70:67:e5:f7:a4:bc:4e:28:51:
         cd:9b:e8:ae:87:9d:ea:d8:ba:5a:a1:01:9a:dc:f0:dd:6a:1d:
         6a:d8:3e:57:23:9e:a6:1e:04:62:9a:ff:d7:05:ca:b7:1f:3f:
         c0:0a:48:bc:94:b0:b6:65:62:e0:c1:54:e5:a3:2a:ad:20:c4:
         e9:e6:bb:dc:c8:f6:b5:c3:32:a3:98:cc:77:a8:e6:79:65:07:
         2b:cb:28:fe:3a:16:52:81:ce:52:0c:2e:5f:83:e8:d5:06:33:
         fb:77:6c:ce:40:ea:32:9e:1f:92:5c:41:c1:74:6c:5b:5d:0a:
         5f:33:cc:4d:9f:ac:38:f0:2f:7b:2c:62:9d:d9:a3:91:6f:25:
         1b:2f:90:b1:19:46:3d:f6:7e:1b:a6:7a:87:b9:a3:7a:6d:18:
         fa:25:a5:91:87:15:e0:f2:16:2f:58:b0:06:2f:2c:68:26:c6:
         4b:98:cd:da:9f:0c:f9:7f:90:ed:43:4a:12:44:4e:6f:73:7a:
         28:ea:a4:aa:6e:7b:4c:7d:87:dd:e0:c9:02:44:a7:87:af:c3:
         34:5b:b4:42

我们可以看到第一个证书cert01.pem的主体名字是acme-demo.beyondstars.xyz,由Let's Encrypt Authority X3签发,而第二个证书cert02.pem的主体名字刚好就是Let's Encrypt Authority X3,由DST Root CA X3签发,你可能问为什么没有从fullchain.pem当中分离出根CADST Root CA X3的证书出来,这是因为跟CA的证书一般是保存在

/etc/ssl/certs

目录的,例如,根CADST Root CA X3的证书存放在

/etc/ssl/certs/DST_Root_CA_X3.pem

我们看一下也无妨

openssl x509 \
-in /etc/ssl/certs/DST_Root_CA_X3.pem \
-noout -text

输出为

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            44:af:b0:80:d6:a3:27:ba:89:30:39:86:2e:f8:40:6b
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: O = Digital Signature Trust Co., CN = DST Root CA X3
        Validity
            Not Before: Sep 30 21:12:19 2000 GMT
            Not After : Sep 30 14:01:15 2021 GMT
        Subject: O = Digital Signature Trust Co., CN = DST Root CA X3
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:df:af:e9:97:50:08:83:57:b4:cc:62:65:f6:90:
                    82:ec:c7:d3:2c:6b:30:ca:5b:ec:d9:c3:7d:c7:40:
                    c1:18:14:8b:e0:e8:33:76:49:2a:e3:3f:21:49:93:
                    ac:4e:0e:af:3e:48:cb:65:ee:fc:d3:21:0f:65:d2:
                    2a:d9:32:8f:8c:e5:f7:77:b0:12:7b:b5:95:c0:89:
                    a3:a9:ba:ed:73:2e:7a:0c:06:32:83:a2:7e:8a:14:
                    30:cd:11:a0:e1:2a:38:b9:79:0a:31:fd:50:bd:80:
                    65:df:b7:51:63:83:c8:e2:88:61:ea:4b:61:81:ec:
                    52:6b:b9:a2:e2:4b:1a:28:9f:48:a3:9e:0c:da:09:
                    8e:3e:17:2e:1e:dd:20:df:5b:c6:2a:8a:ab:2e:bd:
                    70:ad:c5:0b:1a:25:90:74:72:c5:7b:6a:ab:34:d6:
                    30:89:ff:e5:68:13:7b:54:0b:c8:d6:ae:ec:5a:9c:
                    92:1e:3d:64:b3:8c:c6:df:bf:c9:41:70:ec:16:72:
                    d5:26:ec:38:55:39:43:d0:fc:fd:18:5c:40:f1:97:
                    eb:d5:9a:9b:8d:1d:ba:da:25:b9:c6:d8:df:c1:15:
                    02:3a:ab:da:6e:f1:3e:2e:f5:5c:08:9c:3c:d6:83:
                    69:e4:10:9b:19:2a:b6:29:57:e3:e5:3d:9b:9f:f0:
                    02:5d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Subject Key Identifier:
                C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10
    Signature Algorithm: sha1WithRSAEncryption
         a3:1a:2c:9b:17:00:5c:a9:1e:ee:28:66:37:3a:bf:83:c7:3f:
         4b:c3:09:a0:95:20:5d:e3:d9:59:44:d2:3e:0d:3e:bd:8a:4b:
         a0:74:1f:ce:10:82:9c:74:1a:1d:7e:98:1a:dd:cb:13:4b:b3:
         20:44:e4:91:e9:cc:fc:7d:a5:db:6a:e5:fe:e6:fd:e0:4e:dd:
         b7:00:3a:b5:70:49:af:f2:e5:eb:02:f1:d1:02:8b:19:cb:94:
         3a:5e:48:c4:18:1e:58:19:5f:1e:02:5a:f0:0c:f1:b1:ad:a9:
         dc:59:86:8b:6e:e9:91:f5:86:ca:fa:b9:66:33:aa:59:5b:ce:
         e2:a7:16:73:47:cb:2b:cc:99:b0:37:48:cf:e3:56:4b:f5:cf:
         0f:0c:72:32:87:c6:f0:44:bb:53:72:6d:43:f5:26:48:9a:52:
         67:b7:58:ab:fe:67:76:71:78:db:0d:a2:56:14:13:39:24:31:
         85:a2:a8:02:5a:30:47:e1:dd:50:07:bc:02:09:90:00:eb:64:
         63:60:9b:16:bc:88:c9:12:e6:d2:7d:91:8b:f9:3d:32:8d:65:
         b4:e9:7c:b1:57:76:ea:c5:b6:28:39:bf:15:65:1c:c8:f6:77:
         96:6a:0a:8d:77:0b:d8:91:0b:04:8e:07:db:29:b6:0a:ee:9d:
         82:35:35:10

可以看到主体名称和签发者的主体名称都是DST Root CA X3,信任锚一般都是自签的,当然,是由有资质的CA组织自签的,CA自签证书后,会妥善地保管私.我们还可以看到CA证书的有效期比中间CA证书的有效期长一些,而中间CA证书的有效期又比网站TLS证书的有效期长一.但是有意思的是,DST Root CA X3将会在2021年9月30号过期,不知道那么多依赖于Let’s Encrypt免费证书的网站会怎么办.

经过上面这一系列演示,我们看到了,不仅使用浏览器能查看HTTPS连接情况,还能用curl和openssl来查看,而浏览器能显示出证书的详细信息,用openssl也能做到,但是openssl是命令行的,可编程的,肯定更加强大和自由一些.

Let’s Encrypt证书除了可以用acme.sh申请,还可以用certbot申请,certbot还能帮您的NginX服务器设置重定向——把发往80端口的HTTP请求重定向为发往443端口的HTTPS请求,基本上比用acme.sh简单多了.

和用acme.sh申请证书类似,假如说你在服务器x运行certbot申请yoursite.com的TLS证书,那么你要确保yoursite.com会被解析到服务器x,下面我们建立一个域名用于演示如何用certbot申请证书,我们用的是certbot-demo.beyondstars.xyz这个域名,假设这个certbot-demo.beyondstars.xyz域名所指的网站还没有开通,那同样的也要先建立网站根目录和配置NginX服务器

让域名指向网站服务器

让域名指向网站服务器

首先建立网站根目录并配置NginX服务器:

mkdir -p /var/www/certbot-demo.beyondstars.xyz

然后新建一个NginX配置文件

vi /etc/nginx/conf.d/certbot-demo.beyondstars.xyz.conf

输入以下内容

server {
    listen 80;
    root /var/www/certbot-demo.beyondstars.xyz;
    index index.html;
    server_name certbot-demo.beyondstars.xyz;
}

保存并退出文本编辑器,然后执行

nginx -s reload

使Nginx服务器重新加载配置文件使新网站启.如果你不放心新的网站是否已经启用可以尝试

echo "certbot-demo site enabled" >> /var/www/certbot-demo.beyondstars.xyz/index.html
curl http://certbot-demo.beyondstars.xyz/index.html

看到

certbot-demo site enabled

就说明NginX已经成功启用了新的站点并且域名也正确解析.那么接下来就可以使用certbot申请证书并为新的网站启用HTTPS.

安装好certbot之后,首先运行

certbot --nginx

会出现对话界面,选择一个要certbot帮你申请证书的域名

选择要为之申请TLS证书的域名

选择要为之申请TLS证书的域名

这里我们输入数字4,也就是为certbot-demo.beyondstars.xyz这个域名申请TLS证书,然后按回车继续

选择是否重定向HTTP到HTTPS

选择是否重定向HTTP到HTTPS

输入数字2并按回车,certbot将修改NginX配置文件,使HTTP流量自动重定为HTTPS流量,这样网站的HTTPS就启用了,而选数字1我们还要手动配置,这里输入2并按回车就可以了

证书申请成功

证书申请成功

出现这个节目提示证书申请成功并且NginX的HTTP到HTTPS的重定向也配置成功.

可以用curl测试一下HTTPS是否真的启用了

curl https://certbot-demo.beyondstars.xyz/index.html

会看到提示

certbot-demo site enabled

这样就算是成功地用certbot为网站启用了HTTPS了.

certbot和acme.sh都仅仅是一个命令行工具用于申请Let’s Encrypt签发的免费TLS证书,certbot申请得到的证书是存储在

/etc/letsencrypt/live/

目录下,如果你用openssl工具去查看这些证书,你会发现跟我们用openssl去查看acme.sh申请到的证书很类似,根CA和中间CA的证书的主体都是一样的.

自签证书

前面我们简单地学习了一下一些关于公钥加密和公钥签名的知识,从而我们能够理解,我们自己也可以生成私钥,进而生成CA证书并自己对生成的CA证书签名,我们甚至可以自己签发根CA,然后在根CA的基础上签发中间CA,再用中间CA签发网站TLS证书,只不过,由于大家的操作系统上没有安装我们这里生成的自签CA,所以,这种办法不能够代替Let’s Encrypt,也不能够代替付费证书,这种办法,适用于开发环境的部署,开发环境下需要利用HTTPS协议做通信,要测试的组件要用到HTTPS来互相通信,但是我们也可以不必为之大动干戈配置DNS记录指向公网IP然后再申请Let’s Encrypt证书,如果开发环境要用到HTTPS,用自签证书也行,也方便.

首先我们需要生成一个私钥,这个是根CA的私钥,通过以下命令:

openssl genrsa -out root.key.pem 2048

执行完成后目录将多出一个叫root.key.pem的文件,在自签根CA证书之前,需要指定一些配置,首先从

/etc/ssl

目录复制所有后缀名为.cnf的文件到当前文件,然后我们按照这些复制过来的样板文件写出我们自己的配置文件,首先写根CA的配置文件rootca.cnf

[ req ]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no

[ req_distinguished_name ]
countryName	= CN
stateOrProvinceName	= Beijing
localityName = Beijing
0.organizationName = Super Powerful Root CA
organizationalUnitName = I.T. Department
commonName = Super Powerful Root CA
emailAddress = [email protected]

[ v3_ca ]
basicConstraints = CA:true

有了rootca.cnf后就可以自签根CA证书了:

openssl req \
-new -x509 \
-key root.key.pem \
-out root.crt.pem \
-config rootca.cnf 

会得到一个root.crt.pem的文件,这就是根CA的证书文件,主体信息由rootca.cnf配置文件中的填写的信息而定,接下来要生成中间CA的证书,首先生成中间CA的私钥:

openssl genrsa -out intermediate.key.pem 2048

然后准备中间CA的信息,保存为intermediateca.cnf,内容如下:

[ req ]
distinguished_name	= req_distinguished_name
prompt = no

[ req_distinguished_name ]
countryName	= CN
stateOrProvinceName	= Beijing
localityName = Beijing
0.organizationName = Super Powerful Intermediate CA
organizationalUnitName = I.T. Department
commonName = Super Powerful Intermediate CA
emailAddress = [email protected]

然后开始生成中间CA的CSR(Certificate Signing Request):

#!/bin/sh

openssl req \
-new \
-config intermediateca.cnf \
-key intermediate.key.pem \
-out intermediate.csr

然后在用根CA证书对intermediate.csr进行签名之前还要准备一些必要的配置信息,把以下内容填入x509v3.cnf文件

# default settings
CERTPATHLEN		= 1
CERTUSAGE		= digitalSignature,keyCertSign,cRLSign
EXTCERTUSAGE		= serverAuth,clientAuth
CERTIP			= 0.0.0.0
CERTFQDN		= nohost.nodomain

# This section should be referenced when building an x509v3 CA
# Certificate.
# The default path length and the key usage can be overridden
# modified by setting the CERTPATHLEN and CERTUSAGE environment 
# variables.
[x509v3_CA]
basicConstraints=critical,CA:true,pathlen:$ENV::CERTPATHLEN
keyUsage=$ENV::CERTUSAGE

# This section should be referenced to add an IP Address
# as an alternate subject name, needed by isakmpd
# The address must be provided in the CERTIP environment variable
[x509v3_IPAddr]
subjectAltName=IP:$ENV::CERTIP
extendedKeyUsage=$ENV::EXTCERTUSAGE

# This section should be referenced to add a FQDN hostname
# as an alternate subject name, needed by isakmpd
# The address must be provided in the CERTFQDN environment variable
[x509v3_FQDN]
subjectAltName=DNS:$ENV::CERTFQDN
extendedKeyUsage=$ENV::EXTCERTUSAGE

然后用根证书和根证书的私钥对中间证书的CSR进行签名得到中间证书:

openssl x509 \
-req \
-in intermediate.csr \
-out intermediate.crt.pem \
-CA root.crt.pem \
-CAkey root.key.pem \
-CAcreateserial \
-extfile ./x509v3.cnf \
-extensions x509v3_CA

得到中间CA证书文件intermediate.crt.pem. 然后生成网站的私钥:

openssl genrsa \
-out site.key.pem \
2048 

准备网站的信息,保存在site.cnf,如下:

[ req ]
distinguished_name	= req_distinguished_name
prompt = no

[ req_distinguished_name ]
countryName	= CN
stateOrProvinceName	= Beijing
localityName = Beijing
0.organizationName = Internet Company (Beijing Section)
organizationalUnitName = I.T. Department
commonName = yoursite.com
emailAddress = [email protected]

生成网站的CSR:

openssl req \
-new \
-config site.cnf \
-key site.key.pem \
-out site.csr 

用中间CA证书和中间CA的私钥对网站的CSR进行签名得到网站的证书:

openssl x509 \
-req \
-in site.csr \
-CA intermediate.crt.pem \
-CAkey intermediate.key.pem \
-CAcreateserial \
-out site.crt.pem 

得到网站的证书site.crt.pem,然后将网站证书文件和中间CA证书文件拼接得到fullchain.pem(供NginX使用):

cat site.crt.pem intermediate.crt.pem > fullchain.pem

下面是NginX的样例配置

server {
    listen 443 ssl;
    server_name yoursite.com;
    root /var/www/yoursite.com;

    ssl_certificate     /usr/local/etc/nginx/certs.d/yoursite.com/fullchain.pem;
    ssl_certificate_key /usr/local/etc/nginx/certs.d/yoursite.com/site.key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
}

需将fullchain.pem(证书)和site.key.pem(网站私钥)复制到相应位置再使NginX重新加载配置文件

nginx -s reload

亦有包含以上命令的脚本文件可供自动化调用,在

https://github.com/hsiaofongw/openssl-selfsign-ca-demo

同时须知晓较新版本之Google Chrome会将网站显示为不安全,即使在系统中信任了相应地根证书:

Chrome提示证书有效

Chrome提示证书有效

Chrome提示根证书是「受信任」

Chrome提示根证书是「受信任」

以上验证是通过在/etc/hosts文件中将yoursite.com指向127.0.0.1使Chrome访问yoursite.com时向127.0.0.1发起请求实现的,也可以通过openssl手动验证证书

openssl verify -CAfile root.crt.pem intermediate.crt.pem
openssl verify -trusted root.crt.pem -trusted intermediate.crt.pem site.crt.pem

若验证通过,应该会输出

intermediate.crt.pem: OK
site.crt.pem: OK

这就表示自签证书创建成功了.

付费证书

除了Let’s Encrypt证书和自签证书外,还有付费证书,在Let’s Encrypt被熟知之前,付费证书是占主要的,价格相差较大,一般可以在域名注册商那里顺带购买,例如GoDaddy,Gandi.net都提供付费证书签发服务,一些云服务厂商如阿里云,腾讯云亦提供付费证书签发服务,他们可能会要求你生成一个CSR文件,然后把这个CSR文件上传到他们的服务器,签发后会将证书文件发回给你,要注意生成CSR之后保管好私钥,使用时网页服务器中把有关的设置项指向私钥文件和他们发回给你的网站证书文件即.厂商会有具体教程.

总结

在这篇文章中我们先介绍了HTTPS是什么,然后我们讲了一些公钥加密和公钥签名的知识,然后我们讲了HTTPS大概如何实现身份认证和双向加密,然后讲了怎么样获得证书,怎么样部署证书到服务器上,怎么样验证证书,怎么样查看证书信息.希望这篇文章对你有.

感谢!

参考文献

[1] Public-key cryptography - Wikipedia

[2] Transport Layer Security - Wikipedia

[3] HTTPS - Wikipedia

[4] Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile

[5] The Transport Layer Security (TLS) Protocol Version 1.3

[6] X.509 - Wikipedia

[7] Public key infrastructure - Wikipedia

[8] Certification path validation algorithm - Wikipedia

[9] Diffie–Hellman key exchange - Wikipedia

[10] Symmetric-key algorithm - Wikipedia

[11] Automated Certificate Management Environment - Wikipedia

[12] RFC 8555 - Automatic Certificate Management Environment (ACME)

概念普及httpstlscerts

简单数独问题的数学方程式表示

符号链接和硬链接的异同