介绍自签名证书使用中的几个核心概念

写在前面

为了保证连接的安全性,一般实践中会采用 tls 协议进行加密。本文不论证 tls 这种非对称协议的安全性等问题,只涉及在具体实践中会遇到的 ca 证书及其签名。

正文

生成自签名证书的流程

子签名证书涉及到的几个概念

我们可以通过一张图简单示意自签名证书的生成过程,如上图所示。其中 ca-key.pemca.pem 分别表示 ca 证书的私钥以及公钥, server-key.pemserver-cert.pem 则表示被 ca 证书签名的证书(未来用在 server 服务中)的私钥和公钥。图中 server.csr 表示签名证书的请求(certificate signing request)中间文件。

关于 tls 非对称加密协议的几个问题

为什么存在私钥和公钥两个密钥(只要一个密钥行不行呀)

首先网络传输中密钥的存在主要是为了加密端到端的信息传输,避免网络链路中存在第三方监听而消息被窃取。传统的对称加密算法有一个致命的问题——密钥不方便分发;也就是对于网络上的客户端,除非事先和服务端商量好,否则无法在网络中协商密钥(协商的过程被监听,那么协商出来的密钥就没有任何安全意义了)。

非对称加密的概念中,公钥可以在任意网络环境中传输。客户端在发起请求前,只要想办法拿到服务端的公钥 server-cert.pem,就可以使用这个公钥加密自己的请求消息,而被加密的消息只有拥有私钥 server-key.pem 的服务端才可以解密拿到明文请求,即使网络被监听也可以避免明文泄露。

既然有了私钥和公钥为什么还要 ca 证书的参与?

虽然公钥的存在一定程度降低了密钥分发的难度,但是通过非安全的网络拿到公钥的客户端却无法确定自己拿到的公钥是合法的公钥;如果网络中存在拦截,那么拿到的公钥可能是被伪造的,从而导致使用公钥加密的明文消息被泄露。

当存在 ca 证书的时候,因为公钥生成的时候 ca.pem 被签过名(签名信息是一串二进制被附加到证书中),因此客户端可以在拿到公钥 server-cert.pem 后使用自己本地的 ca.pem 证书进行签名验证,如果验证通过则说明拿到的 server-cert.pem 是合法的,从而进行接下来的消息加密、传输。

所谓自签名,其实指的就是用属于自己ca.pem 签名其他业务证书的机制。

如何拿到不属于自己的 ca 公钥证书呢?

互联网上很多公开的服务,比如 https://baidu.comhttps://taobao.com 等,都基于 tls 协议加密信息传输,他们所使用的证书都不是自签名的,为了验证他们服务端返回的公钥的合法性,应该怎么获取对应的 ca 证书进行签名验证呢?其实这个问题也很简单,那就是客户端所在的操作系统已经内嵌了这些证书;比如 android 系统、ios系统、windows系统等在装机的时候就已经包含了这些 ca 证书(一般被称为 ca根证书)。

当我们从互联网上拿到一个公钥,从公钥中拿到签名信息,其中包含签名的 ca 证书信息,只要从本地的证书列表中找到对应的 ca 证书(这些证书属于公钥的一种,而且由于是提前内置的因此我们选择无条件信任,除非你的系统被黑客破坏过),就可以对其进行签名验证了。

证书生成命令行参考

为了方便,这里采用了直接搬了 Protect the Docker daemon socket 文档中的内容。

生成 ca 证书私钥以及公钥

# 生成 ca 私钥
openssl genrsa -aes256 -out ca-key.pem 4096

# 生成 ca 公钥(有效时间 365 天,这里只是用来示例,实际中需要根据具体业务进行选择)
# 且下面命令运行后会要求输入一系列的内容(比如国家、省份、域名、邮箱等)
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
# 

生成业务需要的私钥及签名对应的公钥

# 生成业务(比如服务端)需要的私钥
openssl genrsa -out server-key.pem 4096

# 生成证书签名请求文件(certificate signing request, CSR)
# 下面命令参考 docker 文档,其中的 $HOST 修改成自己 docker daemon 主机的 dns 名
# 非 docker 业务可能不需要
openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr


# tls 连接可以通过域名建立,也可以通过 ip 建立,这里是追加域名和ip信息
# $HOST 同上面的情况
echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >> extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf # docker 专用的吧?
#
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out server-cert.pem -extfile extfile.cnf

查看证书中的内容


openssl x509 -in certificate.pem -text -noout

# -in certificate.pem 指定要查看的PEM证书文件。
# -text 表示输出证书的文本信息。
# -noout 表示不输出证书的编码信息

双向认证

双向认证,顾名思义,客户端和服务器端都需要验证对方的身份,在建立HTTPS连接的过程中,握手的流程比单向认证多了几步。单向认证的过程,客户端从服务器端下载服务器端公钥证书进行验证,然后建立安全通信通道。双向通信流程,客户端除了需要从服务器端下载服务器的公钥证书进行验证外,还需要把客户端的公钥证书上传到服务器端给服务器端进行验证,等双方都认证通过了,才开始建立安全通信通道进行数据传输。(摘自《HTTPS双向认证》)

在实践过程中,大部分采用的都是单向认证,比如通过浏览器发起的大部分请求都是如此。单向认证并不意味着只客户端侧的请求加密而服务端的请求不加密,而只是意味客户端对服务端身份一个方向的验证,服务端则选择无条件相信客户端的合法性(好比现实中开门做生意,顾客怕老板跑路的多,很少有老板怕顾客跑路的)。

双向认证中自签名证书涉及到的几个文件如下图所示: 子签名证书涉及到的几个概念

小结

简单简单介绍了 tls 协议中自签名证书的几个概念。

参考