4

自己做 CA 签发验证 SSL 证书

 9 months ago
source link: https://hsiaofongw.notion.site/CA-SSL-e0cb213042134c408e5c5b84c97e07ea
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
photo-1605153322277-dd0d7f608b4d?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=2400
Drag image to reposition

自己做 CA 签发验证 SSL 证书

Created
December 10, 2023 3:56 AM
openssl
security
Description
一种低成本的安全加固方式。
Direct
Updated
December 11, 2023 7:20 AM
3 more properties

TLS 证书

我们可能有很多应用需要使用传输层安全技术(TLS)来对其中的流量进行加密,比如说我们可能有一个内网的 NAS 后台管理页面在 http://192.168.10.104:10080 提供服务,当我们在内网访问它时并不在意它没有 HTTPS,而假设我们从公网访问它:http://[2116:4246:3a35::ac43:c4f]:10080,我们就需要给它启用 HTTPS,否则在登陆页面输入的用户名、密码就会以明文的形式,被中间所有参与转发的路由器看见,和用户名、密码一样被看见的还有服务端返回的内容,在以前 HTTPS 还没有大规模推广开的时候,个别运营商会劫持并修改网页 HTML 源码,在网页右下角插入一个弹窗。
TLS 它解决了这三个问题:1)如何保证数据传输的保密性;2)如何保证数据传输的完整性;3)如何保证通信主体的真实性;其一,它用对称加密方法加密中间来回传输的流量,使得中间节点只能看到密文,并且中间节点一般拿不到解密密钥把密文恢复成明文。其二,它提供了端到端的安全性,就好像是在两个进程之间建立了一条坚实可靠的「管道」保护着管道里边传输的报文,使得数据在进程到进程之间的传播是完整的,就算被篡改了也是能检验得出的;其三,它通过公钥加密的技术,结合现有的 PKI(公钥密码学基础设施),提供了通信双方验证对端身份真实性的一种机制,例如说,我们通过 https://www.icbc.com.cn 访问工商银行的 web 页面,在 TLS 看来,浏览器是在和一个 IP 地址为 43.152.53.153,端口为 443 的远程进程握手,它怎么知道,这个 43.152.53.153:443 socket 发过来的数据,能代表 www.icbc.com.cn 这个 web 的权威主体?事实上是它通过 TLS 握手机制,来确保这一点,这使得,假如说中间路径上有一个路由器,它尝试通过 104.21.61.2:443 这个地址伪造它的 www.icbc.com.cn 的主体身份,那么本地浏览器就会看到 TLS 握手是和 104.21.61.2:443 进行,而且因为 104.21.61.2 没有 www.icbc.com.cn 的 TLS 证书私钥,所以 TLS 握手会失败,浏览器会识破 104.21.61.2:443 在伪装。
TLS 证书是通过 CA 或者中级 CA 签发的,根 CA 的证书是它自己签发的,中级 CA 的证书是根 CA 签发的,CA 每年会被审计以确保 CA 自己的私钥的安全性是合格的,CA 不会随随便便给任何人任何组织签发 TLS 证书,比如它不会给我签发一个对应 www.icbc.com.cn 这个域名的证书,除非我对此域名的使用权经过了它的验证,这种验证包括不限于,例如说,我对 www.icbc.com.cn 这个域名指向的 Web 服务器的控制权,或者我对这个域名的 DNS 记录的修改的能力等等。所以,如果你看到 43.152.53.153:443 在握手时用的证书是一个合法的 CA 签发的,并且证书中包含了这个 www.icbc.com.cn 这个域名,那么你可以认为他具备对应 www.icbc.com.cn 这个网站的权威性。

证书的取得

公共 PKI

要获得一个 TLS 证书,通常的做法是,自己本地创建一个 CSR (Certificate Signing Request),然后把 CSR 发给中级 CA,会收到中级 CA 对自己网站控制权的验证,例如在某个路径返回什么内容,或者在 DNS 中新建一条给定的 TXT 记录,中级 CA 确认了你对网站的控制权之后,会把证书的共钥,连带它自己的公钥,通过一个可靠的信道发送给你。然后你有了这个 TLS 证书,可以把它放到 Web 服务器中使用,也可以用来给自己开发的应用程序上。然而这种方式它首先要求你有一个域名,以及你有域名指向的 Web 服务器的控制权,其次是时间问题,有的 CA 可能在数分钟内验证完成,例如 Let’s Encrypt,有的可能需要数个工作日。其次是它的扩展性不算好,假设你不是只有一两个应用服务,而是有十几个应用服务需要使用传输层安全技术,那么你可能需要一个泛域名证书 (Wildcard Cert),比如 *.mydomain.com,但是一些人认为泛域名证书存在安全性问题。在集群环境中我们有 cert-manager,它可以自动地为你指定的 Web 服务向 CA 申请、续签 TLS 证书,这也至少需要你有一个域名,不过我认为这是一种不错的方法用来给数十个 Web 服务提供传输层安全性。

私有 PKI

但是有的服务不是运行在集群之内的,或者没条件搭建和维护集群,或者干脆就不舍得花钱买和续费一个域名(域名有便宜的和贵的),或者有的服务干脆就是运行在内网中的没必要用到公共的 PKI 设施,这时候自建 CA 成了一种选项。但是还是要注意,大多数情况下,还是基于公共 PKI 的经过普遍权威认可的 CA 比较稳妥,自建 CA 是为了解决一些边缘情况而存在的,它的需求可能比较小众。
在实操之前,我们需要有一些基础知识。如何验证一个证书 B 是否是证书 A 签发的?这需要用到公钥密码学和数字签名的知识来解释。在对称加密中,通信双方共用一个密钥,这个密钥可以用来加密也可以用来解密,但是在公钥密码学中,密钥分为公钥和私钥,公钥可以公开,而私钥只有私钥的拥有者知道,公钥密码学的应用之一是加密通信:Alice 用 Bob 的公钥对报文进行加密,得到的密文只有使用公钥对应的私钥,也就是 Bob 的私钥才能解开。应用之二是数字签名,假设 Alice 和 Bob 各有对方的公钥,Bob 收到了一封信作者自称是 Alice,Bob 怎样确认这封信真的来自 Alice 呢?Alice 和 Bob 事前约定,发送信件时要加上「签名」,Alice 把自己的私钥匙和信件内容丢进签名函数,签名函数吐出签名值,Bob 把签名值、Alice 的公钥和信的正文丢进签名验证函数,签名验证函数返回 true 或者 false 表示该签名是否有效。算法保证了从私钥得到公钥是容易的,而从公钥反推出私钥几乎是不可做到的。现在我有一个证书 B 和证书 A,要确保证书 B 确实是证书 A 签发的,我们可以在证书 A 上找到它的公钥,拿这个公钥和证书 B 的签名以及哈希值,来验证它是否是证书 A 签发的。至于证书 B 的验证,如果证书 B 是一个中级 CA 证书,那么应该用它上一级 CA 的证书来验证,以此类推这样一层一层地往上验证,直到遇到了一个证书需要用到根 CA 证书并且验证成功,根 CA 证书一般预装在操作系统中,随发行版一起发行。

根证书的创建

终端进程对证书的检验是基于一种「信任链」的模式,也就是:如果我信任一个证书,那么这个证书签发的证书,正常情况下也是可信的。基于信任链的原理,我们可以先签发一个根证书,然后再让我们的私有设备去信任这个根证书,那么后续这个根证书签发的所有次级证书,就都会被自动信任了,这样就不需要每签发/续签一次证书都得给设备手动安装一个证书。
对于这篇文章中讲到的根证书、普通证书,学名都是 X.509v3 证书,区别只在于:1)根证书是自己给自己签发的;2)根证书和普通证书的扩展参数有些不一样。CA 证书也好普通证书也好,都可以统一地用 openssl 的那一套命令行工具来生成、签发、验证和查看,非常的方便。
这篇文章接下来不涉及到 CRL 和 OCSP,感兴趣的朋友可以自行查阅 Wikipedia 和 rfc5280.
首先我们需要生成 CA 自己的私钥:
Shell
我们会得到一个 PEM 格式的私钥,它的默认 permissions 也很有意思:
Shell
权限非常严格。
接下来要开始签发自签证书,按照 openssl 的惯例,我们可以先把证书的一些信息写到一个配置文件中:包括这个证书对应的主体 (subject) 是谁、这个证书是做什么用的、这个证书的唯一标识和签发者的唯一标识等获取方式等:
Plain Text
把它保存为当前目录的 ca.root.ini 文件。
希望进一步了解配置文件的格式以及其中各个字段含义的读者可以参阅 man 5 config 和 man 5 x509v3_config.
然后生成证书:
Shell
得到一个 PEM 格式的 ca.crt 文件位于当前文件夹。
我们可以通过 openssl x509 命令查看此证书的详细信息:
Plain Text
我们发现 X509v3 Subject Key Identifier 和 X509v3 Authority Key Identifier 有相同的值,前者是它自己的唯一编号,后者是签发此证书的证书的唯一编号,再结合 X509v3 Basic Constraints 的 CA:TRUE 字样,使得验证者可以判断它是一个自签的根 CA 证书。
这个 CA 证书的 pathlen 是 2,这意味着它还可以再签发一个 pathlen 等于 1 的中级 CA 证书,然后这个 pathlen 等于 1 的中级 CA 证书自己又可以再签发一个 pathlen 等于 0 的中级 CA 证书,pathlen 等于 0 的 CA 证书不能再签发 CA 证书,只能签发最终用户证书 (end-entity certificate).
我们可以用这行命令验证它:
Shell
接下来我们选择直接用它签发 end-entity certificate。

最终证书的签发

首先生成证书的私钥:
Shell
准备一个 x.com.ini 配置文件,填写主体信息:
Plain Text
接着创建 CSR (certificate signing request):
Shell
可以用下列命令来查看 CSR 信息,确保它已经有了对应的私钥和主体信息:
Shell
然后我们再创建一个 server.crt.ini 文件,保存以下内容,其中 subjectAltName 的 DNS:x.com 对应证书域名 x.com,这个比较重要:
Shell
Shell
在以上过程中,x.com.ini 和 x.com.csr 是申请证书的客户(比如网站站长、应用开发者)生成,server.crt.ini 是签发证书的 CA 生成,x.com.crt 也是 CA 生成。
这时我们再用 openssl x509 -in x.com.crt -noout -text 命令查看生成的证书:
Plain Text
会看到这里已经有了签名值,它的 X509v3 Authority Key Identifier 刚好就是 CA 的 X509v3 Subject Key Identifier,它的 Public Key Modulus 和 CSR 里面的也一样。
我们可以拿 CA 验证这个证书:
Plain Text
在使用这个证书之前我们需要下载根证书,手动添加到操作系统证书目录,并且对它设置信任:
https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F8551cdf7-056d-46dc-82ad-7ab1ba2283ed%2Fca582fcf-c4a6-44a7-affb-4e2cecaeb51e%2F%25E6%2588%25AA%25E5%25B1%258F2023-12-10_%25E4%25B8%258B%25E5%258D%25885.00.21.png?table=block&id=c8fbe67a-9e8a-4df7-85da-312e8e18fde8&spaceId=8551cdf7-056d-46dc-82ad-7ab1ba2283ed&width=960&userId=&cache=v2
这个在不同的操作系统上有不同的做法。
接下来我们可以配置一个测试 nginx server:
Plain Text
把 ssl_certificate 和 ssl_certificate_key 的值改为证书和私钥的实际路径。
执行 sudo nginx -t 测试配置有效性,执行 sudo nginx -s reload 更新配置。
在本地 hosts 配置域名解析,把 x.com 解析到服务器的 IP(v6) 地址,这个在不同操作系统上做法不同,但是对于 *nix 操作系统通常是修改 /etc/host 文件。
做好以上后,以 https scheme 加端口的方式访问:https://x.com:<端口号>
https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F8551cdf7-056d-46dc-82ad-7ab1ba2283ed%2Fedd9fe0d-8a3d-41b1-a9e9-3741a158ee48%2F%25E6%2588%25AA%25E5%25B1%258F2023-12-10_%25E4%25B8%258B%25E5%258D%25885.10.28.png?table=block&id=c56215fd-6ecc-4fe8-a9b4-69ecc2dfcc21&spaceId=8551cdf7-056d-46dc-82ad-7ab1ba2283ed&width=1060&userId=&cache=v2
在 Chrome 浏览器下它默认就是安全的。

总结和后续

我们介绍了一种创建自签 CA 证书的方式,并且介绍了如何使用 openssl 命令行工具创建和签发自己的证书。自签证书的好处是时效性和低成本,我们不需要有自己的服务器或者 Web 虚拟主机空间,也不需要购买域名,也不需要花时间等待 CA 做验证和恢复。
这种自签证书适用于内网 (intranet),尤其是部署安装根 CA 证书的成本很低的情形,安装了根证书后,后续所有由该根证书签发的证书都可以被系统自动信任。
TLS 可以自动使得上层的应用层服务自动地获得端到端的安全性:1)数据完整性;2)保密性;3)和真实性,因此许多应用软件都已提供了对 TLS 的支持,包括最常见的浏览器、数据库命令行工具、远程桌面软件、常驻程序的后台管理界面等。我们可以给每一个这样的服务都签发 TLS 证书,而在终端设备上只需添加信任一次根证书,从而以低成本的方式获得信息安全性提高以及更好的隐私保障。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK