本文为介绍如何使用 OpenSSL 简单生成用于双向认证的密钥和证书。(可能不是特别规范,但足够用于测试和解决基础需求)
注:其实 OpenSSL搭建这样的体系,比较好一点的做法是另一种——用OpenSSL搭建CA,但更复杂一些,若有想法,可以参考这篇文章:OpenSSL命令行:自建CA&操作CRL ,本文不介绍这种方式。
注:当然,搭建这样的体系,更好的做法是使用基于加密机(这就很专业了,就不说了)。
注:本文基于 Windows11 (Linux的话,用的命令应该都一样)
注:本文以常见的 Client-Server 交互为例说明,你可以把主动发起交互的一方当作 Client,另一方接受交互的当作 Server。
一、安装OpenSSL
如何安装以及如何使用,可参考这篇文章:OpenSSL命令行实例 (下文中命令的使用,都能在这篇文章中找到更详细的说明)
注:本文默认已经配置了环境变量,可以在任意目录下执行openssl命令,如果没有配置,则每次执行都需要把 openssl 改为 openssl.exe 的绝对路径。
如:
注:本文编写主要是基于 OpenSSL 3.0.1。理论上按照我之前的研究,下文用到的命令在 1.1.1 版本上应该不会有差异。若有遗漏,望读者指出,先行感谢!
二、新建文件夹
新建文件夹的作用,只是为了看起来更清晰,并无特别的地方。
例如下图是我新建的文件夹(MutualAuthentication),你可以自定义文件夹名字
然后把 openssl.cnf 这个文件复制到这个文件夹中
现在,我们的文件夹里,就有了第一个文件。
统一说明
下文基本都是在执行 OpenSSL命令,执行命令都是在CMD窗口执行控制台命令,所以需要先进入到CMD窗口。
可以在文件夹中空白区域,点击鼠标右键,选择在终端中打开。
也可以使用 Win+R(Windows快捷键),输入 cmd,回车,然后输入 文件夹所在盘符(如:G:),回车,再 cd 文件夹路径(如 cd OpenSSL-Win64\myTest\MutualAuthentication),回车,到达文件夹目录。
文件:
openssl.cnf : OpenSSL配置文件
xxx.key : 密钥文件(本文出现的都是私钥文件),xxx为文件名,可自定义
xxx.csr : 证书请求文件,xxx为文件名,可自定义
xxx.crt : 证书文件,xxx为文件名,可自定义
三、生成 Root证书
3.1 生成密钥
执行命令:openssl genrsa -out root.key 2048
出现新文件:
3.2 生成CSR(V1)
执行命令:openssl req -new -key root.key -out root.csr
过程中需要输入CSR的Subject信息(自行依据实际情况定义),然后看是否要设置密码(设不设置都行):
注:生成CSR时,邮箱地址可设置也可以不设置
生成好以后可以看到对应的文件。
3.3 自签名(V1)
执行命令:openssl x509 -req -days 3650 -sha256 -in root.csr -signkey root.key -out root.crt
执行完可以看到自签名根证书:
双击文件,就可以查看证书信息:
四、生成 子CA
4.1 生成服务端子CA的密钥
执行命令:openssl genrsa -out serverCA.key 2048
4.2 生成服务端子CA的CSR(V1)
执行命令:openssl req -new -key serverCA.key -out serverCA.csr
一样需要输入CSR的相关信息(自行依据实际情况定义):
4.3 用 Root证书 签发 服务端子CA证书(V3)
修改 openssl.cnf 文件(刚刚我们新建文件夹里的那个文件)。
如下图,在 [ v3_ca ] 中注释掉箭头指向的那一行(图中为已注释)。
说明:这里要注释掉的原因是因为父证书是V1证书,没有 KeyIdentifier(使用者密钥标识符),所以 openssl 要把父证书的 KeyIdentifier 复制到子证书里作为 authorityKeyIdentifier(授权密钥标识符)时会失败报错。(所以如果Root证书是V3证书,就不需要修改)
执行命令:openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions v3_ca -in serverCA.csr -CA root.crt -CAkey root.key -CAcreateserial -out serverCA.crt
此时你可以看到 服务端子CA 的三个文件已经集齐:
4.4 用 Root证书 签发 客户端子CA证书(V3)
如果你想偷懒,可以只有一本子CA,即 服务端子CA 和 客户端子CA 是同一个。
如果不弄成同一个,就可以以相似的方式,再生成 客户端子CA。
执行命令:openssl genrsa -out clientCA.key 2048
执行命令:openssl req -new -key clientCA.key -out clientCA.csr
执行命令:openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions v3_ca -in clientCA.csr -CA root.crt -CAkey root.key -CAcreateserial -out clientCA.crt
此时你可以看到 客户端子CA 的三个文件已经集齐:
五、生成 服务端证书
5.1 生成密钥
正常情况下,此步骤在服务器上执行。
执行命令:openssl genrsa -out server.key 2048
5.2 生成CSR(V1)
正常情况下,此步骤在服务器上执行。
执行命令:openssl req -new -key server.key -out server.csr
注:部分场景会严格要求“证书的CN=服务端的IP地址或者域名”,所以如果是这种场景,一定要注意CN的值的设置。
5.3 用 服务端子CA 签发 服务端证书(V3)
正常情况下,此步骤为先从服务器上下载 CSR到本地,然后开始执行签发动作。
一般商业情况下,由权威的CA机构进行签发。
修改 openssl.cnf 文件(刚刚我们新建文件夹里的那个文件)。
将 [ usr_cert ] 替换成下面内容(其实就是设置了 keyUsage 和 extendedKeyUsage、subjectAltName),以及增加了一个 section。
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
keyUsage = critical, Digital Signature, Key Encipherment
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
subjectAltName=@alt_names
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
# This is required for TSA certificates.
# extendedKeyUsage = critical,timeStamping
extendedKeyUsage = critical, TLS Web Server Authentication
[ alt_names ]
IP.1 = 5.6.7.8
其中:
keyUsage : 密钥用法(详见RFC文档:4.2.1.3 Key Usage)
Digital Signature:数字签名
Key Encipherment:密钥加密
subjectAltName:使用者可选名称(作用:允许将其他身份绑定到证书的使用者。详见RFC文档:4.2.1.7 Subject Alternative Name)
注:部分场景(如 Postman)会强制要求填写该字段,要求值包含服务被请求时填写的域名或者IP。
@alt_names:表示这使用的内容为基于名为 alt_names 的section
ExtendedKeyUsage: 扩展密钥用法(详见RFC文档:4.2.1.13 Extended Key Usage)
TLS Web Server Authentication:服务器身份验证
alt_names :自定义的Section,此处用于放置 subjectAltName 的内容
IP.1 = 5.6.7.8 :服务被请求时的IP,假设为 5.6.7.8。
可以在 section 内写多个身份,比如再写一个 IP.2 = 1.2.3.4
执行命令:openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions usr_cert -in server.csr -CA serverCA.crt -CAkey serverCA.key -CAcreateserial -out server.crt
此时,我们就集齐了 服务端证书 相关文件:
六、生成 客户端证书
6.1 生成密钥
正常情况下,此步骤在客户端机器上执行。
执行命令:openssl genrsa -out client.key 2048
6.2 生成CSR(V1)
正常情况下,此步骤在客户端机器上执行。
执行命令:openssl req -new -key client.key -out client.csr
6.3 用 子CA 签发 客户端证书(V3)
正常情况下,此步骤为先从客户端机器上下载 CSR到本地,然后开始执行签发动作。
一般商业情况下,由权威的CA机构进行签发。
修改 openssl.cnf 文件(刚刚我们新建文件夹里的那个文件)。
将 [ usr_cert ] 替换成下面内容(其实就是设置了 keyUsage 和 extendedKeyUsage)。
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
keyUsage = critical, Digital Signature, Key Encipherment
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
# This is required for TSA certificates.
# extendedKeyUsage = critical,timeStamping
extendedKeyUsage = critical, TLS Web Client Authentication
其中:
keyUsage : 密钥用法(详见RFC文档:4.2.1.3 Key Usage)
Digital Signature:数字签名
Key Encipherment:密钥加密
ExtendedKeyUsage: 扩展密钥用法(详见RFC文档:4.2.1.13 Extended Key Usage)
TLS Web Client Authentication:客户端身份验证
执行命令:openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions usr_cert -in client.csr -CA clientCA.crt -CAkey clientCA.key -CAcreateserial -out client.crt
此时我们就集齐了 客户端证书 的相关文件:
七、本文涉及的证书的层级结构
八、生成 信任链
所谓信任链,简单理解就是 信任对方依据的CA链。
例如:服务端要信任来访的客户端,于是就要用提前配置好的信任链来校验双向认证时客户端提供的客户端证书。
注:依据实际进行双向认证的实现不同,信任链有两种可能:
只需要一本颁发者CA证书即可(例如验证客户端,则只需要颁发客户端证书的CA,即 clientCA.crt 即可)
需要从Root到颁发者CA的所有证书(下文是以更常见的这种情况为例)
8.1 生成 服务端配置的信任链
如果是权威 CA机构颁发,一般可以直接获取到。或者操作系统、浏览器等往往会内置权威CA机构的信任链证书,所以无需专门配置。
按照 从 root.crt 到 clientCA 的颁发顺序,将证书内容一一复制到一个文件中(如命名为:serverTrustChain.pem)
注:这个文件将放在服务端,用来验证客户端。
示例:
可以先在本地验证一下看看制作的证书链文件是否可以正常验证客户端证书,执行命令:openssl verify -CAfileserverTrustChain.pem client.crt
8.2 生成客户端配置的信任链
如果是权威 CA机构颁发,一般可以直接获取到。或者操作系统、浏览器等往往会内置权威CA机构的信任链证书,所以无需专门配置。
按照 从 root.crt 到 serverCA.crt 的颁发顺序,将证书内容一一复制到一个文件中(如命名为:clientTrustChain.pem)
注:这个文件将放在客户端,用来验证服务端。
示例:
可以先在本地验证一下看看制作的证书链文件是否可以正常验证服务端证书,执行命令:openssl verify -CAfile clientTrustChain.pem server.crt