6

什么是 HTTPS 的证书信任链?自己给自己发行不行?

 2 years ago
source link: https://www.51cto.com/article/716351.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

什么是 HTTPS 的证书信任链?自己给自己发行不行?-51CTO.COM

什么是 HTTPS 的证书信任链?自己给自己发行不行?
作者:zxg_神说要有光 2022-08-15 08:06:16
HTTPS 就是在 TCP 和 HTTP 之间加了一个 SSL 或者叫 TLS 层,实现了加密、身份认证、防篡改的功能。
8887c5e96ba68c998cb138697979c6f9646f2b.jpg

互联网应用的网络通信一般都是通过 HTTP,但 HTTP 是明文传输的,容易泄漏信息,所以大多数应用都会升级为 HTTPS。

HTTP 底层是用 TCP 传输的,HTTPS 就是在 TCP 和 HTTP 之间加了一层加密和认证的协议,这一层叫做 SSL/TLS。

图片

为什么叫这个名字呢?

因为最早的时候是 SSL 协议,但是后来发现了漏洞,就改为 TLS 协议了,而且 TLS 协议也在不断的升级,从 1.1、1.2 到了现在的 TLS 1.3。

不管叫 SSL 还是叫 TLS,都是指的这一层。

这一层就实现了加密、身份认证,还有防篡改的功能。

加密很容易理解,就是通过一种加密算法对内容进行处理,生成密文,然后另一端通过解密算法把密文处理成原始内容。

但只有加解密算法还不行,得加入一些随机性,让每次都不一样,所以会有密钥的存在,每次先随机生成密钥,然后通信过程就用这个密钥配合加密算法来进行加解密。

这个密钥是随机生成的,所以只能一端生成以后告诉另一端。

那么问题来了,怎么把这个密钥安全的告诉另一端呢?

这就得用到一种特殊的加密算法 --- 非对称加密了。

这种加密算法特殊在有两个密钥,用一个密钥加密的数据只能另一个密钥解密,那么把一个密钥暴露出去,一个密钥留下,这样用留下的密钥加密的数据,别人都能解密,但是用暴露出去的密钥加密的数据,只有自己能解密。

这个暴露出去的密钥就叫做公钥,留下的密钥叫做私钥。

图片

这样当别人想给你传递一些信息的时候,就通过你暴露出去的公钥对信息加密,别人都解密不了,只有你能解密。这就保证了信息传递的安全性。

就比如说上面提到的密钥传递问题,就可以通过这种公私钥的非对称加密机制来解决。

有的同学可能会问,既然基于公私钥的非对称加密机制能保证安全,为啥还要再传递别的密钥呢?

因为这种非对称加密太慢了,传递几次信息还行,频繁的用这种方式加解密数据的话效率太低了。所以一般只用这种方式来传递会话密钥,保证安全,然后后面用传递的会话密钥来进行数据加解密。

前面提到 TLS 层主要是实现了加密、身份认证、防篡改的功能。

加密是用对称加密的方式,用到的密钥通过基于公私钥的非对称加密机制来传递。

那身份认证怎么做呢?

其实也是通过公私钥的机制,刚才提到了公钥加密的内容只能私钥解密,这保证了信息的安全传递。

那反过来,私钥加密的数据,如果用公钥能解开,这不就证明了信息是你传递的么?因为私钥只有你有。

所以,私钥的加密又叫做签名,可以用来做身份的认证。

那用私钥加密什么呢?

一般是对传输的信息做一次 hash,生成数据指纹,然后用私钥加密这个数据指纹,也就是对它进行签名。

这样数据传递到另一端,用公钥把数据指纹取出来,再对内容做一次 hash,生成一份数据指纹,两者对比一下,如果一样,就说明没有被篡改。

这就是 TLS 层的第三个功能,防篡改,也就是保证数据的完整性。

至此,HTTPS 给 HTTP 额外添加的加密、身份认证、防篡改功能的实现原理我们都了解了。

但不知道同学们有没有发现这其中有个漏洞,非对称加密的算法是公开的,你可以生成公私钥,别人也可以,那怎么保证我拿到的公钥是你的呢?

万一我拿到的公钥是别人的,那我用它加密的数据,不就被别人截去了么?

想解决这个问题就涉及到一个新的概念,数字证书了:

现在的问题是怎么验证公钥是某个人的。

如果我有信得过的人,他说这个公钥是那个人的,我就相信。基于这样的信任来验证可以么?

也就是说我信任的人有自己的公私钥,他用私钥对这段信息签名,我收到信息后用他的公钥来解密,发现能解密出其中的信息,说明这是被他签名过的,我就相信我收到的公钥是可靠的。

图片

这样是可以的,但怎么保证我收到的信任的人的公钥是真的呢?

这就无限循环起来了。

现实中肯定不会这样无限循环的,解决方式是操作系统内置了一批信得过的机构的公钥,经过这些机构签名的,就一定是对方的公钥。

这种信得过的机构叫做 CA(Certification Authority),电子认证机构,经过 CA 认证的公钥和相关信息,就叫做数字证书。

操作系统内置了所有可信的 CA 的证书,也就是 CA 的公钥和相关信息,叫做根证书。

在 mac 下可以在钥匙串里看到系统内置的所有根证书:

图片

当你打开一个 https 的网站的时候,会把网站的证书下载下来,看看是不是经过这些系统内置过的 CA 根证书所信任的证书,如果是,代表收到的网站的公钥是可信的。

证书都是颁给某个域名的,也就是证明证书里的公钥是这个域名的。

比如打开 https://baidu.com ,查看它的证书:

图片

你会看到这样的三级证书,根证书是系统内置的 CA 的证书,它信任了一个中间的证书,然后这个中间证书信任了 baidu.com 的证书。(信任就是指用自己的私钥做了签名)

这个 CA颁发的根证书是内置在系统里的,受信任的,所以也就也就信任了他信任的中间证书,从而信任了中间证书信任的 baidu.com 的证书,这是一条信任链。

图片

你打开 taobao.com 也会看到是这样的三级证书链:

图片

为什么都是三级呢?

因为这样万一中级证书不能信任了,还可以让根证书再找一个中级证书,因为信任根证书,也自然信任这个新的中级证书,但如果根证书直接信任某个网站的证书,万一根证书被攻破不能信任了,那就找不到可以信任的了。

所以,三级证书会更安全一些。

也就是这样的一条信任链:

图片

也就是说想给网站升级 HTTPS,得找 CA 申请个证书才行。

一般的云计算平台都提供了代理申请的服务,不过一年的费用挺贵的,比如阿里云:

图片

如果我只是想测试下 HTTPS,要花这么大成本去 CA 申请个证书么?

那倒不用,我们可以自己创建一个 CA 根证书,然后用它给自己颁发证书,这叫自签名证书:

自签名证书

当测试的时候,可以用 openssl 这个库自己创建一个 CA 根证书。

第一步用 genrsa 命令生成私钥:

openssl genrsa -out ca-key.pem -des 1024

过程中要输入口令,这个是保护私钥用的。

图片

然后用 req 命令创建证书签名请求,输入域名和相关信息:

openssl req -new -key ca-key.pem -out ca-csr.pem

这个过程中要输入一些信息,最重要的是域名信息,因为证书就是为了证明公钥是这个域名的:

图片

然后用 x509 命令生成根证书:

openssl x509 -req -in ca-csr.pem -signkey ca-key.pem -out ca-cert.pem

至此,根证书创建完了,产生了 ca-key.pem、ca-csr.pem、ca-cert.pem 三个文件,分别是私钥、证书签名请求、根证书。

然后用这个根证书创建网站的证书。

也是同样的三步:

用 genrsa 生成私钥:

openssl genrsa -out server-key.pem 1024

然后用 req 命令创建证书签名请求。


openssl req -new -key server-key.pem -out server-csr.pem

最重要的也是域名信息:

图片

最后一步生成证书,但是这里要指定前面生成的 QA 的根证书:

openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req

这样就产生了 server-key.pem、server-csr.pem、server-cert.pem 三个文件,分别是网站的私钥、证书签名请求、证书。

用私钥和证书就可以创建 https 服务了,我们使用 nodejs 来创建:

const https = require('https');
const fs = require('fs');

let options = {
  key: fs.readFileSync('./server-key.pem'),
  cert: fs.readFileSync('./server-cert.pem')
};

https.createServer(options,function(req,res){    
    res.write('hello https');
    res.end();

}).listen(3000);

然后浏览器访问,就可以看到 https 服务返回的内容:

图片

chrome 会标记为不安全,我们点开看一下证书:

会看到只有一级 www.ggg.com 的证书,还没有被信任。

图片

这是因为签发他的根证书没有导入钥匙串,我们导入一下:

图片

导入 ca-cert.pem,就可以在钥匙串中找到 guangguangguang.com 的根证书,已经标记了是自签名证书:

图片

再访问网站,就会看到二级的结构了:

图片

但是还没有被信任,我们信任一下自签名的根证书:

图片

再去网站看一下,就可以看到证书受信任了,因为颁发他的根证书受信任了:

图片

不过网站依然会标记为不安全,这是 chrome 的策略,不支持自签名证书。

一般我们还是会向 CA 机构申请一个证书的,但是测试的时候可以自行创建一个 CA 根证书,然后给自己颁发网站的证书。

HTTPS 就是在 TCP 和 HTTP 之间加了一个 SSL 或者叫 TLS 层,实现了加密、身份认证、防篡改的功能。

为了增加随机性,每次都要生成密钥来做加解密,传输这个密钥需要用到非对称加密的公私钥机制。

公钥加密的内容只有私钥能解开,防止被窃取。

私钥只有一个人有,所以加密的内容可以用作身份认证,也就是签名。

对内容做 hash,然后私钥签名,就能做到完整性校验,防止被篡改。

但是如何保证拿到的公钥一定是对方的,这是个复杂的问题。

现在的方案是系统内置了一些 CA 的根证书,然后这些 CA 证书颁发了一些网站的证书,如果访问网站拿到的证书是这些 CA 机构颁发的,那就是受信任的。

不过现实中一般都是三级的证书信任链,增加安全性。

向 CA 申请证书可以用阿里云之类的云计算提供商的代理服务,但都挺贵的,如果测试的话,可以用 openssl 自己创建一个 CA 根证书,自己给自己签名,这叫做自签名证书。

理解了这条证书信任链,也就理解了 HTTPS 的核心。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK