哈哈哈没想到勤劳勇敢的键者居然会那么快又开始写下一篇吧,在大年初一那么美好的一天,所以我们来讨论一下网络工程中的数字证书方案吧。

谨在此祝各位童鞋新的一年大吉大利,狗年旺旺~

咳咳(正经脸.webp),在《从数字签名到数字证书》中,键者简单的整理了从数字签名到数字证书的理论基础及推导逻辑,那么这篇其实就是工程上某种实现数字证书的签发管理……

OpenSSL

首先需要简要介绍的就是名为openssl的工具,简单说,它是SSL/TLS的开源实现,实现了相关的加密解密算法、具备数字证书管理等功能的一个程序。

至于安装方式嘛,相信难不倒各位童鞋的,键者秉持偷懒的心略过……

我们先来简单回顾下自建CA需要处理些什么问题?

  • 我们需要能生成不对称密钥
  • 我们需要能基于公钥及一些基础信息生成证书签名请求
  • 我们需要能用私钥证书签名请求签名,从而生成数字证书

为了方便,以下可能会使用CSR描述证书签名请求CRT表示数字证书

生成RSA密钥

那么,我们首先认识一下生成RSA密钥的命令吧:

  1. openssl genrsa -out -des3 rsa.key 4096
  2. + `genrsa` 用于生成 RSA 密钥对的 OpenSSL 命令。
  3. + `-out` 令生成的密钥对保存到文件
  4. + `4096` RSA 模数位数
  5. + `-des3` 使用 3-DES 对称加密算法加密密钥对,该参数需要用户在密钥生成过程中输入一个口令用于加密。今后使用该密钥对时,需要输入相应的口令。如果不加该选项,则不对密钥进行加密。

此时,会生成rsa.key文件,但注意,这个可不只是私钥哦,里边还有包括pq在内的其他参数,所以通过ca.key直接推导出公钥

虽然理论上,RSA算法中的公钥私钥是没有本质区别的,仅知道任何一个都无法推导另一个出来,但如果有了pq等参数就不一样了。
所以请妥善保管ca.key,这个文件其实等价于同时包含了公钥私钥

如果使用了-des3命令,则生成时会要求用户输入一个密码,并用该密码对生成的文件内容再做一次加密;除了-des3以外,还可以选择-aes128-aes256等加密算法,相当于为生成的rsa.key再加了一层保险。

不过用的时候也要相应地使用密码解密就是了。

在整个数字证书体系中,可以认为,每一个实体都会唯一持有一对不对称密钥,这对密钥也是后续所有数字证书相关功能的身份验证基础。

这个实体可能是CA,也可能是Server或者Client
目前工程上常用是RSA算法,但并不是只有RSA哦,有兴趣的童鞋可以自己去搜搜看~

生成CSR

如果说密钥实现了身份的鉴别基础,那么CSR其实本质上就是更具体的身份信息,就像基因可能唯一标识了一个人,但这个人的社会属性如姓名经历
等信息也是构成这个人不可或缺的部分。

所以生成CSR的时候,需要同时打包公钥基本信息

  1. openssl req -new -days 365 -key rsa.key -out someone.csr
  2. + `req` 用于生成证书请求的 OpenSSL 命令。
  3. + `-new` 生成一个新的证书请求。该参数将令 OpenSSL 在证书请求生成过程中要求用户填写一些相应的字段。
  4. + `-days 365` 证书有效时间。
  5. + `-key` 用于签名的CA私钥。
  6. + `-out` 将生成的请求保存到文件。

执行这个命令的时候,会提示要求输入国家代码(Country Name)等信息,这是数字证书标准中要求CSR附带的基本信息,所以根据自己的情况填写即可,完成后即可得到someone.csr

其中填写Common Name时注意,这里请填写的运行服务的主机可以正确解析的域名,本地调试的话可以使用localhost,否则可能会导致基于这个数字证书的网络服务(如,https)无法工作。

注意,这里虽然传入了rsa.key,但其实本质上是为了将rsa.pub写入someone.csr中,以便后续生成证书,并不是把私钥写入了someone.csr中哦。

签名并生成CRT

生成证书其实就是用私钥证书请求进行签名。

使用的私钥可以是自己的,也可以是别人的。如果是自己的,则表示自签名,就像我为自己带盐一样。如果是别人的,则表示别人为这个证书请求作担保。

一般这里的别人都承担着CA的职能。

  1. openssl x509 -req -in someone.csr -signkey rsa.key -out rsa.crt
  2. - `x509` 操作符合x509标准的数字证书
  3. - `-req` 传入一个`证书请求`,对其签名并输出`证书`
  4. - `-int` 待签名的`证书请求`文件。
  5. - `-signkey` 用于签名的私钥。
  6. - `-out` 将生成的证书保存到文件

x509其实是数字证书的文件格式标准,一般我们用的数字证书都是符合x509标准的。

小结

应用的关键在于:

  • 要信任谁,则表现为信任其证书,本质是信任证书的签发机构。
  • 要为谁做担保,则使用私钥为其请求签名,请求应包含被担保人的公钥。

实战:我们来实现一组双向认证的数字证书体系吧

前文三个命令已经涵盖了数字证书管理体系中最核心的三个步骤,后续更复杂的参数其实也只是数字证书特性的灵活应用,所以现在我们来试着设计一个稍微复杂点的结构。

其实这才是键者之前在折腾的东西……

假设我们现在有一个Server和一个Client,每当Client连接Server的时候,Server需要确认Client有自己信任的CA签发的CRT,而Client也需要确认Server有自己信任的CA签发的CRT

理论上这两个CA可以是同一个,也可以是不同的两个,为了复杂起见Orz,吖不,为了强调两者并没有关联性,我们来设计一个不同的场景,以下会称作SRV.CACLI.CA,分别表示SRV信任的CACLI信任的CA

那么我们可以简单梳理出,这个体系中有四个实体,分别是SRV.CACLI.CASRVCLI

生成CA

那么我们先来看一下,怎么生成一个自签名的CA,因为生成过程其实是一样的,只是用途不同。

  1. # 生成CA的`密钥对`
  2. openssl genrsa -out ca.key 4096
  3. # 生成CA的`证书请求`
  4. openssl req -new -key ca.key -out ca.csr
  5. # 用CA的私钥为自己的`证书请求`签名,生成`数字证书`
  6. openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

其实*.crt包含的核心内容就是基础信息公钥

根据上述代码,我们可以轻松获得两个CA,以及它们的密钥数字证书,我们现在已有的文件如下:

  1. |- client.ca.key
  2. |- client.ca.crt
  3. |- server.ca.key
  4. \- server.ca.crt

CSR其实只是中间文件,可以保留,也可以删除,这里先省略了。

ServerClient签发证书

先梳理一下思路,因为SRV.CASRV信任的证书签发机构,所以,要用SRV.CAClient签发数字证书;同理,Client.CA其实是给Server签发证书的机构。

那么我们先来看看Server侧的生成代码:

  1. # 生成Server的`密钥对`
  2. openssl genrsa -out server.key 4096
  3. # 生成Server的`证书请求`
  4. openssl req -new -key server.key -out server.csr
  5. # 用Client.CA的私钥为Server的`证书请求`签名,生成`数字证书`
  6. openssl x509 -req -in server.csr -signkey client.ca.key -out server.crt

这里与前边的自签名证书就不同了,用于签名的私钥变成了client.ca.key

同理,我们可以推导出Client侧的签发代码:

  1. # 生成Client的`密钥对`
  2. openssl genrsa -out client.key 4096
  3. # 生成Client的`证书请求`
  4. openssl req -new -key client.key -out client.csr
  5. # 用Server.CA的私钥为Client的`证书请求`签名,生成`数字证书`
  6. openssl x509 -req -in client.csr -signkey server.ca.key -out client.crt

这样,我们就可以得到以下的文件集合:

  1. |- client.ca.key
  2. |- client.ca.crt
  3. |- server.ca.key
  4. |- server.ca.crt
  5. |- client.key
  6. |- client.crt
  7. |- server.key
  8. \- server.crt

木有错,这些就是我们完成整套工作时需要的文件,作为管理者,我们再看看分发后的场景:

  1. \- ServerDirectory
  2. |- server.ca.crt
  3. |- server.key
  4. \- server.crt
  5. \- ClientDirectory
  6. |- client.ca.crt
  7. |- client.key
  8. \- client.crt

实际工程中,我们应该仅分发足够应用的证书文件,特别是保护好私钥,私钥一旦泄漏,整个信任体系就会面临崩溃,特别是CA的私钥。

小结

在去年的故事里,键者唠唠叨叨流水账了一波数字证书的工作机制,那么今年的第一篇文章,就来具体讲讲怎么样通过OpenSSL生成具备生产价值的数字证书……

其实键者一开始想写的只是怎么用golang实现gRPC的基于TLS的双向认证,没想到,故事越写越长,估计这个故事又要等下一期了……

相信有悟性高的童鞋已经发现了,其实CAClientServer的身份都只是相对的,Server的私钥同样可以签发新的证书,真正起作用的是其工作时承担的角色而已。CA还可以继续签发CA,从而形成一条证书链,证书链可以实现分层的授权管理。

当然,OpenSSL其实有更详细的参数,可以约束一个签发的证书仅可用于充当CAServer或者Client等等,但只要理解其核心原理,万变不离其宗,相信已经不需要键者啰嗦咯~

最后的最后,其实管理各种证书是个很麻烦的事情,既牵涉到业务逻辑,又涉及各种密码的存管。别的不说光起名字就是个麻烦事,而且随着服务端、客户端的扩容,需要管理的证书只会多不会少,这块东西其实也是个问题。

键者目前是使用了一个叫xca的软件来管理证书,如果有童鞋有更好的推荐欢迎在评论中告诉键者~

本篇关键词

TLS:Transport Layer Security,传输层安全协议
SSL:Secure Sockets Layer,可以认为是TLS的前身。