# CA & OpenSSL自签名证书

# 什么是CA

CA（Catificate Authority）就是签证机构，负责签发证书、认证证书、管理已颁发证书的机关。它的作用就是提供证书（即服务器证书，由域名、公司信息、序列号和签名信息组成），加强服务端和客户端之间信息交互的安全性，以及证书运维相关服务。

任何个体/组织都可以扮演 CA 的角色，只不过难以得到客户端的信任，能够受浏览器默认信任的 CA 大厂商有很多，其中 TOP CA 有 Symantec、Comodo、Godaddy、GolbalSign、Digicert 和 Let's Encrypt。

## 证书是什么

这里的证书指的是数字证书，是在互联网通讯中标志通讯各方身份信息的一个数字认证，可以在网上用它来识别对方的身份。

最简单的证书包含一个公开密钥、名称以及CA的数字签名。一般情况下证书中还包括密钥的有效时间，发证机关(证书授权中心)的名称，该证书的序列号等信息。

数字证书的通用格式基于 [X.509标准](https://zh.m.wikipedia.org/wiki/X.509)。 X.509证书里含有公钥、身份信息（比如网络主机名，组织的名称或个体名称等）和签名信息（可以是证书签发机构CA的签名，也可以是自签名）。

当个人，网站或组织希望获取数字证书时，他们会提交证书签名请求（CSR）以及要验证的公开密钥和信息。 然后，公共信任的CA将验证信息并使用中级密钥签名 链 到受信任的根证书。 然后，根据颁发的证书类型，可以将颁发的证书用作网站，客户端身份验证，代码签名，文档签名等凭证。

## 根证书

根证书 **Root Certificate** 是属于根 CA 的公钥证书，是在公开密钥基础建设中，信任链的起点。一般情况下被信任的根证书会被预先安装到用户的系统或浏览器中。也可以自己手动安装，但是手动安装不安全，不建议手动安装。

Mac 用户可以在 `钥匙串>系统根证书` 里查看系统内置的根证书，权威 CA 的根证书一般都会内置在系统里。

这里的根证书指的都是公钥证书，既然有公钥证书那么就有对应的私钥证书，放在一起就叫 `Root Pair`。

## 中级 CA

为了安全，Root CA 的私钥证书一般都是不联网的，所有的签名都是在线下由人手动签名，这样是为了确保不会有黑客攻击等方法导致私钥证书泄漏，从而出现大量假的签名证书。但是域名何止千千万万，每个都要由人手动签名效率肯定非常低，而且也没法一一认证，所以在 Root CA 下面又引入了**中级 CA**（Intermediate CAs），由中级 CA 作为 Root CA 的代表为服务器和客户端签证。

为了验证证书是否可信，必须确保证书的颁发机构在设备的可信CA中。如果证书不是由可信CA签发，则会检查颁发这个CA证书的上层CA证书是否是是可信CA，客户端将重复这个步骤，直到证明找到了可信CA（将允许建立可信连接）或者证明没有可信CA（将提示错误）。

点击左上角地址栏的锁符号，可以看到网站的证书，就像我的博客的证书如下，根证书是 互联网安全研究小组ISRG 提供的 `ISRG Root X1`，中级是由 Let's Encrypt 提供的 `R3` 中级证书。

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1651402055124/zO6bCfrIp.png align="center")

## SSL/TLS 证书

每个域名都可以申请一个 SSL证书，SSL是 **S**ecure **S**ockets **L**ayer（安全套接层协议）的缩写。由网景公司再1994年随HTTPS一起推出。后交由[IETF](https://zh.wikipedia.org/wiki/IETF)将SSL进行标准化，并改名为 TLS，**T**ransport **L**ayer **S**ecurity（传输层安全性协议）。所以 TLS 是 SSL 改个名而已，其实是一个东西。

SSL包含记录层（Record Layer）和传输层，记录层协议确定传输层数据的封装格式。传输层安全协议使用 [X.509](https://zh.wikipedia.org/wiki/X.509) 认证，之后利用非对称加密演算来对通信方做身份认证，之后交换对称密钥作为会谈密钥（Session key）。这个会谈密钥是用来将通信两方交换的资料做加密，保证两个应用间通信的保密性和可靠性，使客户与服务器应用之间的通信不被攻击者窃听。具体加密过程可以看我 [上一篇博客](https://foreverz.cn/https) 了解。

### 证书类型

证书分为三种类型：

- DV（Domain Validation），中文全称是域名验证型证书，面向个体用户，安全体系相对较弱，证书审核方式为通过验证域名所有权即可签发证书，可以通过邮箱验证、DNS验证或者文件验证；
- OV（Organization Validation），中文全称是企业验证型证书，面向企业用户，证证书审核方式为通过验证域名所有权和申请企业的真实身份信息才能签发证书。CA 通过拨打信息库中公司的电话来确认；
- EV（Extended Validation），中文全称是增强验证型证书，证书审核级别为所有类型最严格验证方式，在OV类型的验证基础上额外验证其他企业的相关信息，比如银行开户许可证书。打开 Github 的网页，你会看到 URL 地址栏展示了注册公司的信息，这会让用户产生更大的信任，这类证书的申请除了以上两个确认外，还需要公司提供金融机构的开户许可证，要求十分严格。

具体可以看[DV型、OV型、EV型证书的主要区别](https://www.myssl.cn/home/article-0406-61.html)了解。

# 自签名证书

下面讲一下什么是 OpenSSL，如何使用 OpenSSL 自签名证书。

## OpenSSL

OpenSSL是一个开放源代码的软件包，应用程序可以使用这个包来进行安全通信，避免窃听，同时确认另一端连接者的身份。其主要库是以C语言所写成，实现了基本的加密功能，实现了SSL/TLS协议。

OpenSSL 非常强大，据说支持四百多个命令，而且像 Mac、Linux 的很多版本都是内置了 OpenSSL，在 自建 CA 时我们可以使用到 OpenSSL 的以下功能：

- 生成和管理公钥，私钥以及相关生成参数。
- 使用公钥和私钥，加密，解密，签名，验证。
- 管理证书（X.509格式）认证请求和证书吊销列表。
- 计算摘要（hash），支持各种摘要算法。
- 使用各种加密方法(cipher)加密，解密。
- 测试SSL和TLS连接。
- 对email签名或加密（S/MIME）格式。
- 生成，验证时间戳。

### 加密

先说一下 OpenSSL 支持的加密算法。

#### 对称加密算法
OpenSSL一共提供了8种对称加密算法，其中7种是分组加密算法，仅有的一种流加密算法是RC4。这7种分组加密算法分别是AES、DES、Blowfish、CAST、IDEA、RC2、RC5，都支持电子密码本模式（ECB）、加密分组链接模式（CBC）、加密反馈模式（CFB）和输出反馈模式（OFB）四种常用的分组密码加密模式。其中，AES使用的加密反馈模式（CFB）和输出反馈模式（OFB）分组长度是128位，其它算法使用的则是64位。事实上，DES算法里面不仅仅是常用的DES算法，还支持三个密钥和两个密钥3DES算法。

#### 非对称加密算法
OpenSSL一共实现了4种非对称加密算法，包括DH算法、RSA算法、DSA算法和椭圆曲线算法（EC）。DH算法一般用于密钥交换。RSA算法既可以用于密钥交换，也可以用于数字签名，当然，如果你能够忍受其缓慢的速度，那么也可以用于数据加密。DSA算法则一般只用于数字签名。

### Hash

OpenSSL实现了5种哈希算法，分别是MD2、MD5、MDC2、SHA（SHA1）和RIPEMD。SHA算法事实上包括了SHA和SHA1两种信息哈希算法，此外，OpenSSL还实现了DSS标准中规定的两种信息哈希算法DSS和DSS1。

哈希一般有两个作用：1）做信息完整性校验；2）保存密码，有些密码是直接在数据库中采用MD5（真实密码值）保存的，有的还进行加盐处理，使其难以破解，这样密码只能重置，无法告诉你原始过程，因为哈希是不可逆的。

### 几个常用的子命令及其参数

#### genrsa

`genrsa` 平时主要用来生成私钥，可以选择使用的算法、对称加密密码和私钥长度来生成私钥。

```shell
$ openssl genrsa [...args] [numbits]
```

`genras` 支持以下参数：

* args1：对生成的私钥文件是否要使用加密算法进行对称加密，支持以下加密算法：
  - `-des`: CBC模式的DES加密
  - `-des3`: CBC模式的3DES加密
  - `-aes128`: CBC模式的AES128加密
  - `-aes192`: CBC模式的AES192加密
  - `-aes256`: CBC模式的AES256加密
* `-passout pass`：对称加密(des、3des、aes)的密码。
* `-out outputfilepath`：输出证书私钥文件，一般为 `ca.key.pem`。
* `numbits`：密钥长度，可以理解为私钥长度，一般为 2048 或 4096。

如下生成一个2048位的RSA私钥，并用`aes256`加密(密码为123456)，保存为`ca.key.pem`文件

```shell
$ openssl genrsa -aes256 -passout pass:123456 -out ca.key.pem 4096
```

#### req

`req` 的基本功能主要有两个：生成证书请求和生成自签名证书。

```shell
$ openssl req [...args] outfile
```

* `-inform arg`：输入文件的格式：
    - DER：使用输入文件格式为DER。
    - PEM：使用输入文件格式为PEM。
* `-outform arg`：输出文件的格式：
    - DER：使用输出文件格式为DER。
    - PEM：使用输出文件格式为PEM。
* `-in inputfilepath`：输入文件路径。
* `-out outputfilepath`：证书的保存路径。
* `-passin arg`：用于签名待生成的请求证书的私钥文件的解密密码。
* `-new`：表示生成一个新证书签署请求，此时会忽略`-in`指定的内容。
* `-nodes`：表示不对私钥进行加密，如果指定此选项，则忽略`-passout`指定的口令。
* `-x509`：根据现有的证书请求生成自签名根证书（要求使用`-key`指定证书请求里面的公钥相应的私钥，以便对自签名根证书进行签名）。
* `-key file`：生成请求时用到的私钥文件，如果不指定此选项会根据`-newkey`选项的参数生成密钥对。
* `-keyform arg`：指定输入密钥的编码格式：
    - DER
    - NET
    - PEM
* `-newkey rsa:bits`：生成一个bits长度的RSA私钥文件，用于签发【生成私钥、并生成自签名证书】，只有在没有`-key`选项的时候才生效
* `-days num`：证书的有效期限，单位是day（天），该选项只有在使用了`-x509`选项生成自签名证书的时候才生效，默认为30天。
* `-subj args`：直接从指令行指定证书请求的主体名称，格式为`/`分割的键值对字符串，如果没有此选项，那么会弹出交互提示；eg：`/CN=example.ca.com`。
* 设置HASH算法：
    - `-sha256`
    - `-md5`
    - `-md2`
    - `-mdc2`
    - `-md4`
* `-config file`：指定OpenSSL配置文件，很多内容不容易通过参数配置，可以指定配置文件，一般默认为`openssl.cnf`。
* `-text`：显示格式txt【用于查看证书、私钥信息】
* `-extensions v3_ca`：选项指定了生成自签名根证书的时候使用的扩展字段，其参数为OpenSSL配置文件中的某个字段名。

下面看几个 Demo：

```shell
# 生成一个新的证书请求，使用新的rsa2048位密钥，输出证书请求到request.pem，密钥到private.pem,私钥口令为12345678
$ openssl req -new -newkey rsa:2048 -keyout private.pem -passout pass:12345678 -out request.pem

# 对证书请求签名进行验证
$ openssl req -in request.pe -verify -noout

# 生成一个自签名的根证书
$ openssl req -x509 -newkey rsa:2048 -keyout private.pem -passout pass:12345678 -out selfsign.crt

# 显示证书请求内容
$ openssl req -text -in fd.csr -noout
```

#### ca

`ca`指令模拟一个完整的CA服务器，它包括签发用户证书，吊销证书，产生CRL及更新证书库等管理操作。

```shell
$ openssl ca [...args]
```

* `-config configfilepath`: 指定要使用的配置文件，如果没有此选项，则会先查找 `OPENSSL_CONF` 或者 `SSLEAY_CONF` 定义的文件名，如果这两个环境变量都没有定义，就使用OpenSSL安装的默认路径，一般是 `/usr/local/openssl/openssl.cnf`，具体看安装配置。
* `-startdate args`: 设置证书的生效时间，格式为`YYMMDDHHMMSSZ`指定年月日时分秒，如果没有则使用主配置文件中的`default_startdate`。
* `-enddate`: 设置证书的失效时间，格式跟`-startdate`一样。
* `-days num`: 设置证书的有效天数，生效时间到到期时间之间的天数，如果使用了`-enddate`，此选项被忽略。
* `-name str`: 替换配置文件指定的`default_ca`所表示的内容。
* `-notext`: 不输出明文信息到证书文件。
* `-md arge`: 加密算法，可以通过 `-passin` 参数指定私钥口令。
* `-passin arg`：指定私钥口令来源。
* `-subj args`: 直接从指令行指定证书请求的主体名称，格式为`/`分割的键值对字符串，如果没有此选项，那么会弹出交互提示；eg：`/CN=example.ca.com`。
* `-cert certfilepath`: 参数是一个可以包含路径的文件名，该文件是一个PEM编码的X.509证书文件。
* `-keyfile keyfilepath`: 参数是一个包含路径的文件名，文件格式可以为PEM，DER，PKCS#12，Netscape，IIS SGC，Engine，但需要通过`-keyform`指定到底是哪种格式。
* `-policy args`: 指定CA的匹配策略，arg为配置文件中的策略段。
* `-extensions args`: 指定x509 v3扩展字段的字段名，如果没有这个选项就由`-extfile`中内容。
* `-extfile extfilepath`: 指定x509 v3扩展的配置文件，如果没有`-extensions`字段，则由CA主配置文件中的`x509_extensions`选项指定。
* `-in inputfilepath`: 输入的文件，被用于CA中心签名的证书请求文件路径。
* `-infiles files`: 指定一系列包含PEM编码证书请求的文件，包含多个，只能作为指令的最后一个选项，其后的参数都被认为是证书请求文件。
* `-out outputfilepath`: 选项指定了输出签发好的证书或者新生成的CRL的文件，如果没有使用`-notext`选项，那么证书的明文信息也会输出到`-out`选项指定的文件中。
* `-outdir path`: 选项指定了新生成的证书的输出目录，默认输出到`newecerts`目录，并使用`.pem`作为后缀，都是PEM编码。



#### x509

`x509` 是一个功能很丰富的证书处理工具。可以用来显示证书的内容，转换其格式，给CSR签名等 X.509 证书的管理工作。

```shell
$ openssl x509 [args]
```

* `-inform arg`：输入文件格式。
    - DER：使用输入文件格式为DER。
    - PEM：使用输入文件格式为PEM。
* `-outform arg`：输出文件格式。
    - DER：使用输出文件格式为DER。
    - PEM：使用输出文件格式为PEM。
* `-in inputfilepath`：待处理X509证书文件。
* `-out outputfilepath`：待输出X509证书文件。
* `-req`：表明输入文件是一个"请求签发证书文件(CSR)"，等待进行签发。
* `-days num`：签名证书的有效时间。
* `-CA file`：指定用于签发请求证书的根CA证书。
* `-CAform arg`：根CA证书格式(默认是PEM)。
* `-CAkey file`：指定用于签发请求证书的CA私钥证书文件。
* `-CAkeyform arg`：根CA私钥证书文件格式(默认是PEM)。
* `-CAserial file`：指定序列号文件(serial number file)。
* `-CAcreateserial`：如果序列号文件(serial number file)没有指定，则自动创建。
* 设置HASH算法：
    - `-sha256`
    - `-md5`
    - `-md2`
    - `-mdc2`
    - `-md4`

验证证书：

```shell
$ openssl x509 -noout -text -in certs/ca.cert.pem

# 正确输出包含以下内容
# 数字签名（Signature Algorithm）
# 有效时间（Validity）
# 主体（Issuer）
# 公钥（Public Key）
# X509v3 扩展，openssl config 中配置了 v3_ca，所以会生成此项
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            87:e8:c0:a0:4b:e2:12:5d
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=Zhejiang, O=Barret Lee, OU=Barret Lee Certificate Authority, CN=Barret Lee Root CA
        Validity
            Not Before: Apr 23 05:46:36 2016 GMT
            Not After : Apr 18 05:46:36 2036 GMT
        Subject: C=CN, ST=Zhejiang, O=Barret Lee, OU=Barret Lee Certificate Authority, CN=Barret Lee Root CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (4096 bit)
                Modulus (4096 bit):
                    // ...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                E5:2D:B8:2B:DC:88:FE:CE:DA:93:D8:6F:2E:74:04:D2:39:E7:C8:03
            X509v3 Authority Key Identifier:
                keyid:E5:2D:B8:2B:DC:88:FE:CE:DA:93:D8:6F:2E:74:04:D2:39:E7:C8:03

            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
    Signature Algorithm: sha256WithRSAEncryption
        // ...
```

#### rsa

`rsa` 一般用于从私钥中抽出公钥。

```shell
$ openssl rsa [...args]
```

- `-in inputfilepath`：私钥文件
- `-out outputfilepath`：公钥保存路径
- `passin arg`：对称加密(des、3des、aes)的密码

```shell
# 通过私钥导出公钥
$ openssl rsa -in private.pem -pubout -out public.pem

# 将私钥转成明文
$ openssl rsa -in private.pem -text -out private.txt
```

## 几种文件格式

在制作自签名证书的过程中会涉及到几个不同类型的证书文件，这里统一说明以下，防止被绕晕了：

* `.pem`: 采用 PEM 编码格式的 X.509 证书的文件扩展名；全称是 Privacy-enhanced Electronic Mail，是明文格式的内容，以 `-----BEGIN CERTIFICATE-----` 开头，以 `-----END CERTIFICATE-----` 结尾，中间是经过base64编码的内容。查看这类证书的信息的命令为`openssl x509 -noout -text -in xx.pem`。

* `.der`：是二进制编码的证书，查看这类证书的信息的命令为`openssl x509 -noout -text -inform der -in xx.der`。

* `.crt`：即 certificate 的缩写，证书文件 ，常见于类 UNIX 系统，可以是DER（二进制）编码的，也可以是PEM（ ASCII (Base64) ）编码的 ，但绝大多数情况下此格式证书都是采用 PEM 编码。

* `.cer`：也是 certificate 的缩写，常见于 Windows 系统，编码类型同样可以是DER或者PEM的，但绝大多数情况下此格式证书都是采用 DER 编码。

* `.csr`：Certificate Signing Request 的缩写，即证书签名请求，它并不是证书的格式，而是用于向权威证书颁发机构（Certificate Authority, CA）获得签名证书的申请，一般是生成请求以后发送给CA，然后CA会给你签名并发回证书。其核心内容包含一个 RSA 公钥和其他附带信息，在生成这个 .csr 申请的时候，同时也会生成一个配对 RSA 私钥，私钥通常需要严格保存于服务端，不能外泄。 

* `.key`：一般公钥或者密钥都会用这种扩展名，可以是DER编码的或者是PEM编码的，查看DER编码的（公钥或者密钥）的文件的命令为 `openssl rsa -inform DER -noout -text -in xxx.key`；查看PEM编码的（公钥或者密钥）的文件的命令为 `openssl rsa -inform PEM -noout -text -in xxx.key`。

* `.p12`：也写作 `.pfx`，全称：[PKCS #12](https://en.wikipedia.org/wiki/PKCS_12)，是公钥加密标准（Public Key Cryptography Standards，PKCS）系列的一种，它定义了描述个人信息交换语法（Personal Information Exchange Syntax）的标准，可以用来将包含了公钥的 X.509 证书和证书对应的私钥以及其他相关信息打包，进行交换。简单理解：一份 `.p12` 文件 = X.509 证书+私钥；

* `.cnf`：这种文件一般是各种软件程序使用的配置文件，用于保存软件参数、用户设定以及各种配置选项，通常以纯文本格式保存。这里通常是 OpenSSL 的配置文件，详细可以在 [这里](https://www.openssl.org/docs/manmaster/man5/config.html) 了解。

## 使用 OpenSSL 生成证书

### 创建 Root Pair

Root Pair 即根证书的公钥和私钥，创建 Root Pair 需要在绝对安全的环境下，可以断开网络、拔掉网线和网卡，如果是在测试环境则无所谓。

首先我们创建一个 `root` 文件夹，用来存放根证书相关的文件：

```shell
$ mkdir root
```

#### 创建 Root Key

可以使用 `genrsa` 命令创建 Root key，如下创建一个4096位的RSA私钥，并用`aes256`加密(密码为123456)，保存为`root/ca.key.pem`文件

```
$ openssl genrsa -aes256 -passout pass:123456 -out root/ca.key.pem 4096
```

#### 创建 Root Cert

这一步就是创建根证书了，需要通过 `req` 子命令来创建，而且需要一个 Root CA 的 openssl.cnf 配置文件，我这里准备好了一个放在了 GitHub 上，可以下载下来自己修改：

```shell
$ curl -o ./root/openssl.cnf https://gist.githubusercontent.com/foreverzmy/0b07d3a731d70519e35ced070118e3fe/raw/71210c2643b8997ea6e2cf333e26aa33e712a7e2/simple-root-ca.cnf

$ openssl req -config root/openssl.cnf \
      -key root/ca.key.pem \
      -passin pass:123456 \
      -new \
      -x509 \
      -days 7300 \
      -sha256 \
      -extensions v3_ca \
      -out root/ca.cert.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=JediRootCA/emailAddress=921255465@qq.com
```

这样创建的 `root/ca.cert.pem` 就是我们所说的根证书了。

#### 验证证书

可以通过`x509`命令来验证证书的有效性

```shell
$ openssl x509 -noout -text -in root/ca.cert.pem
```

是否有效可以看上面 `x509` 命令的介绍。

### 创建 Intermediate Pair

前面我们已经拥有了 Root Pair，事实上已经可以用于证书的发放了，但是由于根证书很干净，特别容易被污染，所以我们需要创建中级 Pair 作为 Root Pair 的代理，生成过程同上，只是细节略微不一样。

创建一个 `intermediate` 目录用来存放中级 Pair 相关的文件：

```shell
$ mkdir intermediate
```

#### 创建 Intermediate Key

继续使用 `genrsa`  命令创建 Intermediate Key，即中级CA的私钥：

```shell
$ openssl genrsa -aes256 -passout pass:123456 -out intermediate/intermediate.key.pem 4096
```

#### 创建 Intermediate Cert

同样使用 `req` 子命令来创建 Intermediate Cert，即中级CA证书，这里需要特别注意的一点是 Common Name(subj 参数的 CN 字段) 不要与 Root Pair 的一样，而且也需要一个 Intermediate CA 的 openssl.cnf 配置文件：

```shell
$ curl -o ./intermediate/openssl.cnf https://gist.githubusercontent.com/foreverzmy/936b1bb55adf52c5d1accc9b2216a0e3/raw/28b9f97df967d93febf09219863190aea3b36895/simple-intermediate-ca-cnf

$ openssl req -config intermediate/openssl.cnf -new -sha256 \
      -key intermediate/intermediate.key.pem \
      -passin pass:123456 \
      -out intermediate/intermediate.csr.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=JediIntermediateCA/emailAddress=921255465@qq.com
```

### 使用 Root CA 给 Intermediate Cert 签名

会依赖两个文件，我们需要先创建一下，然后进行签名：

```shell
$ touch root/index.txt
$ echo 1000 > root/serial

$ openssl ca -config root/openssl.cnf \
      -extensions v3_intermediate_ca \
      -days 3650 \
      -notext \
      -md sha256 \
      -passin pass:123456 \
      -in intermediate/intermediate.csr.pem \
      -out intermediate/intermediate.cert.pem
```

`intermediate/intermediate.cert.pem` 就是创建的中级CA证书了。

此时 `root/index.txt` 中将会多出这么一条记录：

```
V	320428133311Z		1000	unknown	...
```

#### 验证 Intermediate Cert

同样可以通过`x509`命令来验证证书的有效性，也可以使用根证书来验证中级证书有效性：

```shell
$ openssl x509 -noout -text -in intermediate/intermediate.cert.pem
$ openssl verify -CAfile root/ca.cert.pem intermediate/intermediate.cert.pem
```

### 创建证书链

浏览器在验证中级证书的时候，同时也会去验证它的上一级证书是否靠谱，创建证书链，将 Root Cert 和 Intermediate Cert 合并到一起，可以让浏览器一并验证：

```shell
$ cat intermediate/intermediate.cert.pem root/ca.cert.pem > intermediate/ca-chain.cert.pem
```

此时两个证书会合并到 `intermediate/ca-chain.cert.pem` 文件中。


### 创建服务器/客户端证书

这一步是生成我们服务器上需要部署的内容，Root Pair 和 Intermediate Pair 使用的都是 4096 位的加密方式，一般情况下服务器/客户端证书的过期时间为一年，所以可以安全地使用 2048 位的加密方式。

这里为我的博客域名 `foreverz.cn` 生成证书，先创建一个 `foreverz.cn` 的文件夹：

```shell
$ mkdir foreverz.cn
```

#### 创建域名私钥

继续使用 `genrsa`  命令创建 `foreverz.cn` 这个域名的私钥：

```shell
$ openssl genrsa -aes256 -passout pass:123456 -out foreverz.cn/foreverz.cn.key.pem 2048
```

#### 创建域名证书

```shell
$ openssl req -config intermediate/openssl.cnf \
      -key foreverz.cn/foreverz.cn.key.pem \
      -passin pass:123456 \
      -new -sha256 \
      -out foreverz.cn/foreverz.cn.csr.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=foreverz.cn/emailAddress=921255465@qq.com
```

#### 使用中级证书签证域名证书

同样会依赖两个文件，需要先创建一下，然后进行签证：

```shell
$ touch intermediate/index.txt
$ echo 1000 > intermediate/serial

$ openssl ca -config intermediate/openssl.cnf \
      -extensions server_cert \
      -days 375 \
      -notext \
      -md sha256 \
      -in foreverz.cn/foreverz.cn.csr.pem \
      -passin pass:123456 \
      -out foreverz.cn/foreverz.cn.cert.pem
```

此时 `intermediate/index.txt` 中也会多出这么一条记录：

```
V	230511135530Z		1000	unknown	...
```

#### 验证域名证书

使用 `x509` 和证书链来验证域名证书：

```
$ openssl x509 -noout -text -in foreverz.cn/foreverz.cn.cert.pem
$ openssl verify -CAfile intermediate/ca-chain.cert.pem foreverz.cn/foreverz.cn.cert.pem
```

可以看到以下内容说明证书创建成功：

```shell
foreverz.cn/foreverz.cn.cert.pem: OK
```

最后我们会得到以下三个文件

* `ca-chain.cert.pem`：CA 证书文件，需要安装到电脑上验证域名证书的有效性。
* `foreverz.cn.key.pem`：域名私钥文件，在服务器启用 HTTPS 的时候需配置此文件。
* `foreverz.cn.cert.pem`：域名公钥文件，在服务器启用 HTTPS 的时候需配置此文件。

### 完整命令

```shell
#!/bin/bash
set -ex

# 创建 Root Pair

# 创建 root 文件夹存放 Root Pair
mkdir root

# 创建 Root Key
openssl genrsa -aes256 -passout pass:123456 -out root/ca.key.pem 4096

chmod 400 root/ca.key.pem

curl -o ./root/openssl.cnf https://gist.githubusercontent.com/foreverzmy/0b07d3a731d70519e35ced070118e3fe/raw/71210c2643b8997ea6e2cf333e26aa33e712a7e2/simple-root-ca.cnf

# 创建 Root Cert
openssl req -config root/openssl.cnf \
      -key root/ca.key.pem \
      -passin pass:123456 \
      -new \
      -x509 \
      -days 7300 \
      -sha256 \
      -extensions v3_ca \
      -out root/ca.cert.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=JediRootCA/emailAddress=921255465@qq.com

chmod 400 root/ca.cert.pem

# 验证证书
# openssl x509 -noout -text -in root/ca.cert.pem

# 创建 Intermediate Pair

# 创建 intermediate 文件夹存放 Intermediate Pair
mkdir intermediate

# 创建 Intermediate Key
openssl genrsa -aes256 -passout pass:123456 -out intermediate/intermediate.key.pem 4096

chmod 400 intermediate/intermediate.key.pem

curl -o ./intermediate/openssl.cnf https://gist.githubusercontent.com/foreverzmy/936b1bb55adf52c5d1accc9b2216a0e3/raw/28b9f97df967d93febf09219863190aea3b36895/simple-intermediate-ca-cnf

# 创建 Intermediate Cert
openssl req -config intermediate/openssl.cnf -new -sha256 \
      -key intermediate/intermediate.key.pem \
      -passin pass:123456 \
      -out intermediate/intermediate.csr.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=JediIntermediateCA/emailAddress=921255465@qq.com

chmod 400 intermediate/intermediate.key.pem

touch root/index.txt
echo 1000 > root/serial
# 使用 v3_intermediate_ca 扩展签名，密码为 123456
# Intermediate Pair 的有效时间一定要为 Root Pair 的子集
openssl ca -config root/openssl.cnf \
      -extensions v3_intermediate_ca \
      -days 3650 \
      -notext \
      -md sha256 \
      -passin pass:123456 \
      -in intermediate/intermediate.csr.pem \
      -out intermediate/intermediate.cert.pem

# 验证 Intermediate Pair
# openssl x509 -noout -text -in intermediate/intermediate.cert.pem
# openssl verify -CAfile root/ca.cert.pem intermediate/intermediate.cert.pem

# 浏览器在验证中级证书的时候，同时也会去验证它的上一级证书是否靠谱
# 创建证书链，将 Root Cert 和 Intermediate Cert 合并到一起，可以让浏览器一并验证
cat intermediate/intermediate.cert.pem root/ca.cert.pem > intermediate/ca-chain.cert.pem
chmod 444 intermediate/ca-chain.cert.pem

# 创建服务器/客户端证书

# 创建 foreverz.cn 存放域名相关证书文件
mkdir foreverz.cn

# Root pair 和 intermediate pair 使用的都是 4096 位的加密方式
# 一般情况下服务器/客户端证书的过期时间为一年，所以可以安全地使用 2048 位的加密方式
openssl genrsa -aes256 -passout pass:123456 -out foreverz.cn/foreverz.cn.key.pem 2048

# 创建网站证书
openssl req -config intermediate/openssl.cnf \
      -key foreverz.cn/foreverz.cn.key.pem \
      -passin pass:123456 \
      -new -sha256 \
      -out foreverz.cn/foreverz.cn.csr.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=foreverz.cn/emailAddress=921255465@qq.com


touch intermediate/index.txt
echo 1000 > intermediate/serial

# 使用 intermediate pair 签证网站证书
openssl ca -config intermediate/openssl.cnf \
      -extensions server_cert \
      -days 375 \
      -notext \
      -md sha256 \
      -in foreverz.cn/foreverz.cn.csr.pem \
      -passin pass:123456 \
      -out foreverz.cn/foreverz.cn.cert.pem

# 验证证书
# openssl x509 -noout -text -in foreverz.cn/foreverz.cn.cert.pem
openssl verify -CAfile intermediate/ca-chain.cert.pem foreverz.cn/foreverz.cn.cert.pem

# CA 证书链：ca-chain.cert.pem
# 网站私钥：foreverz.cn.key.pem
# 网站公钥：foreverz.cn.cert.pem
```

至此，完结撒花🌹🌹🌹


# 参考

1. [OpenSSL Certificate Authority](https://jamielinux.com/docs/openssl-certificate-authority/index.html)
1. [什么是数字证书？](https://www.ssl.com/zh-CN/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/%E4%BB%80%E4%B9%88%E6%98%AF%E6%95%B0%E5%AD%97%E8%AF%81%E4%B9%A6/)
2. [自签名证书和CA证书的区别和制作、使用](https://www.cnblogs.com/zhaobowen/p/13321578.html)
3. [细说 CA 和证书](https://www.barretlee.com/blog/2016/04/24/detail-about-ca-and-certs/)
4. [OpenSSL自签名证书](http://mjpclab.site/linux/openssl-self-signed-certificate)
5. [使用OpenSSL生成多域名自签名证书进行HTTPS开发调试](https://zhuanlan.zhihu.com/p/26646377)
6. [https://www.jianshu.com/p/fb2ae3dc7986](OpenSSL 介绍和使用)
7. [OpenSSL-维基百科](https://zh.wikipedia.org/wiki/OpenSSL)
8. [openssl的介绍和使用](https://segmentfault.com/a/1190000014963014)
9. [OpenSSL命令--ca](https://blog.csdn.net/as3luyuan123/article/details/13346613)

