CentOS 8 搭建符合 RFC 5280 的三级 PKI 证书体系
1. 项目概述:在 CentOS 8 上亲手搭起一座可信的数字门禁系统
你有没有遇到过这样的场景:公司内部要部署一套安全的远程办公接入系统,所有员工设备必须先“验明正身”才能连进内网;或者开发团队需要为微服务之间通信建立双向身份认证,避免任意服务冒充他人调用接口;又或者你正在做网络安全实验,老师布置的任务是“构建一个完整的公钥基础设施(PKI)环境”。这时候,你真正需要的不是一堆加密命令的堆砌,而是一个可验证、可复用、可审计的证书颁发中心(ЦС,即 Certificate Authority,CA)——它就是整个信任链的起点,是数字世界里的“公安部户籍科”,负责签发、吊销、验证每一张数字身份证(X.509 证书)。
这个标题“Создание и настройка центра сертификации (ЦС) в CentOS 8”直译是“在 CentOS 8 中创建并配置证书颁发中心”,但它的实际分量远不止于此。它代表的是对 PKI 底层逻辑的一次完整实践:从根密钥的安全生成、自签名根证书的权威确立,到中间 CA 的分层设计、终端实体证书的签发流程,再到证书吊销列表(CRL)的发布与验证机制。这不是调用一个 API 就能搞定的黑盒操作,而是需要你亲手控制每一个信任锚点的建立过程。
我做过不下二十个 PKI 实验环境,从 Ubuntu 到 Rocky Linux,再到这次的 CentOS 8(注意:不是 CentOS Stream,而是已停止维护但仍在大量生产环境运行的 CentOS 8.5/8.6),最深的体会是:操作系统版本决定工具链边界,而工具链边界直接决定你能否干净利落地完成一次符合 RFC 5280 标准的证书生命周期管理。CentOS 8 默认搭载 OpenSSL 1.1.1k,它支持 TLS 1.3、Ed25519 签名、OCSP Stapling 等现代特性,但不支持 FIPS 140-2 模式下的某些算法(如 SHA-1 已被默认禁用);它自带的certutil和pk12util来自 NSS 工具集,与 OpenSSL 并存但互不兼容;而 Easy-RSA 3.x 虽然轻量,却在 CentOS 8 的 Python 3.6+ 环境下存在路径解析 bug,必须打补丁才能正常工作。这些细节,文档里不会写,但实操中一步踩错,你就得重头来过。
这篇文章面向三类人:一是正在备考 RHCE 或 Security+ 的工程师,需要一份可直接用于实验复现的完整指南;二是企业运维人员,手头有一台老旧但稳定的 CentOS 8 物理机或虚拟机,想快速搭建一个内部 CA 为 OpenVPN、StrongSwan 或 Nginx mTLS 提供支撑;三是高校学生,在《网络安全原理》课程中需要交一份有逻辑、有验证、有截图的 PKI 配置报告。全文不讲抽象理论,只讲你在终端里敲下的每一行命令背后的意图、参数取舍的理由、以及那些只有亲手试过才会知道的“坑”。比如为什么根 CA 私钥必须用-aes-256-cbc加密而不是-des3?为什么openssl ca命令里default_days = 3650是安全与运维成本的平衡点?为什么 CRL 分发点(CRL Distribution Point)必须配置为 HTTP URL 而不能是本地文件路径?这些答案,都在接下来的实操细节里。
2. 整体架构设计与方案选型逻辑
2.1 为什么坚持用原生 OpenSSL 而非 Easy-RSA?
看到标题里没提 Easy-RSA,你可能会疑惑:网上九成教程都用 Easy-RSA,它不是更简单吗?没错,Easy-RSA 是一个封装良好的脚本集合,能帮你自动创建目录结构、生成配置模板、执行openssl req和openssl ca命令。但它最大的问题是抽象过度,掩盖了底层逻辑。举个真实例子:某次我帮客户排查一个证书无法被 Java 应用信任的问题,最终发现是 Easy-RSA 生成的根证书缺少basicConstraints = critical, CA:true扩展项,而 Java 的keytool在验证时对此极为严格。但 Easy-RSA 的vars配置文件里根本找不到这个字段的开关,你得去翻它的 Python 源码,再手动修改easyrsa3/pki/templates/ca.cnf—— 这已经超出了“快速上手”的范畴,变成了二次开发。
而原生 OpenSSL 方案,虽然初始配置稍显繁琐,但优势极其明确:
- 完全可控:每一个扩展项(
subjectKeyIdentifier、authorityKeyIdentifier、keyUsage、extendedKeyUsage)都由你通过openssl.cnf显式定义,不存在“默认行为不可知”的风险; - 标准兼容:直接遵循 IETF RFC 5280 和 NIST SP 800-57,生成的证书能被 Windows CryptoAPI、OpenSSL、GnuTLS、Java KeyStore 全部正确识别;
- 调试友好:当
openssl verify -verbose server.crt报错时,你能精准定位是CA certificate key too weak(密钥长度不足2048位)、还是error 20 at 0 depth lookup: unable to get local issuer certificate(根证书未导入信任库),而不是面对 Easy-RSA 的./easyrsa sign-req server server一行报错无从下手。
所以本方案选择纯 OpenSSL + 手动openssl.cnf配置作为核心引擎,仅将 Easy-RSA 作为“参考实现”用于对比验证——比如用 Easy-RSA 生成一套证书,再用本文方法生成另一套,用openssl x509 -in ca.crt -text -noout对比二者扩展字段的差异,从而反向理解每个配置项的作用。
2.2 为何采用三级 CA 层级结构而非单级?
很多入门教程只建一个根 CA,然后直接给所有服务器签发终端证书。这在实验室里没问题,但在真实环境中是重大安全隐患。原因在于:根 CA 私钥必须离线保存,永不联网。一旦根私钥泄露,整个信任体系瞬间崩塌,所有已签发证书作废,你需要通知所有依赖方更新信任根——这在大型组织中几乎是不可能完成的任务。
因此,我们采用经典的Root CA → Intermediate CA → End Entity三层结构:
- Root CA(根证书颁发机构):仅用于签发中间 CA 证书,私钥永久离线存储(例如刻录到光盘、存入保险柜),绝不参与日常签发;
- Intermediate CA(中间证书颁发机构):部署在一台受控的 CentOS 8 服务器上,负责签发所有服务器证书、客户端证书、代码签名证书等;即使该服务器被攻破,只需吊销中间 CA 证书,重新签发新中间 CA 即可,根证书不受影响;
- End Entity(终端实体):Nginx、OpenVPN、Postfix 等具体服务使用的证书,由 Intermediate CA 签发,有效期通常设为1-2年,便于轮换。
这种设计不是为了炫技,而是遵循了 X.509 PKI 的最佳实践(RFC 3647)。CentOS 8 的 OpenSSL 完全支持此模型,关键在于openssl.cnf中ca段的policy和copy_extensions配置,以及openssl ca命令中-extensions v3_intermediate_ca参数的精确使用。后面会详细展开。
2.3 工具链锁定:为什么是 OpenSSL 1.1.1k,而不是更新的 3.x?
CentOS 8 默认仓库提供的是 OpenSSL 1.1.1k(2021年3月发布),而 OpenSSL 官网已推出 3.0.x 和 3.2.x。看起来升级是理所当然的,但实测下来,在 CentOS 8 上强行编译安装 OpenSSL 3.x 是一条高风险路径。原因有三:
- ABI 不兼容:OpenSSL 3.x 引入了全新的 Provider 架构,彻底重构了 EVP 接口。CentOS 8 系统中大量基础组件(如
curl、wget、systemd-journald)都动态链接着/lib64/libcrypto.so.1.1和/lib64/libssl.so.1.1。如果你用make install覆盖了系统库,会导致ssh登录失败、yum命令崩溃等一系列连锁故障; - 包管理冲突:
dnf update会检测到 OpenSSL 库被手动修改,可能触发强制回滚,让你的环境陷入半瘫痪状态; - 功能冗余:OpenSSL 1.1.1k 已完整支持 TLS 1.3、X25519 密钥交换、Ed25519 签名、以及所有必需的 X.509 扩展项。OpenSSL 3.x 新增的 FIPS 140-3 支持、国密 SM2/SM3/SM4 算法等,在 CentOS 8 的默认生态中并无对应集成,属于“有劲使不出”。
因此,本方案严格锁定系统原生 OpenSSL 1.1.1k,并通过openssl version -a命令确认其编译参数(尤其是--with-rand-seed=os,devrandom确保熵源可靠)。所有后续操作,包括密钥生成、CSR 创建、证书签发,均基于此版本验证通过。
2.4 目录结构设计:安全与可维护性的双重考量
一个混乱的目录结构,是 PKI 环境夭折的第一原因。我见过太多人把所有密钥、证书、CRL 都扔在/root/ca/下,结果半年后自己都分不清哪个server.key对应哪台机器。本方案采用FHS(Filesystem Hierarchy Standard)兼容的分层目录设计,既符合 Linux 管理规范,又便于权限隔离:
/etc/pki/CA/ # 主配置与根 CA 数据(需 root 权限) ├── private/ # 根 CA 私钥(权限 0400,属主 root) │ └── cakey.pem # 根私钥(AES-256-CBC 加密) ├── certs/ # 所有已签发证书(权限 0444) │ ├── ca.crt # 根证书(PEM 格式) │ └── intermediate.crt # 中间 CA 证书 ├── crl/ # 证书吊销列表(权限 0444) │ └── ca.crl # 根 CA 的 CRL(空文件,仅占位) ├── newcerts/ # 中间 CA 签发的证书存档(权限 0755) │ └── 01.pem # 第一张签发证书 ├── serial # 下一个证书序列号(文本文件,权限 0644) ├── index.txt # 证书索引数据库(权限 0644) └── openssl.cnf # 主配置文件(权限 0644) /opt/pki/intermediate/ # 中间 CA 工作区(可由普通用户操作) ├── private/ # 中间 CA 私钥(权限 0400,属主 causer) │ └── intermediate.key.pem ├── csr/ # 待签发的证书请求(权限 0644) │ └── nginx.csr ├── certs/ # 中间 CA 签发的证书(权限 0444) │ └── nginx.crt ├── crl/ # 中间 CA 的 CRL(权限 0444) │ └── intermediate.crl ├── newcerts/ # 签发存档(权限 0755) ├── serial # 中间 CA 序列号 ├── index.txt # 中间 CA 索引 └── openssl.cnf # 中间 CA 配置(继承自根 CA 配置)这个结构的关键设计点在于:
- 权限分离:根 CA 私钥锁死在
/etc/pki/CA/private/,只有 root 可读;中间 CA 私钥放在/opt/pki/intermediate/private/,可由专用用户causer管理,避免 root 权限滥用; - 路径标准化:所有路径遵循 Linux 标准,
/etc/pki/存放系统级 PKI 配置,/opt/pki/存放应用级 PKI 数据,便于备份和迁移; - 可审计性:
index.txt是一个纯文本数据库,每行记录证书状态(V=有效、R=吊销、E=过期)、序列号、过期时间、CN 名称,可用grep、awk直接查询,无需启动数据库服务。
提示:不要试图把整个
/etc/pki/CA/目录用chown -R causer:causer改属主。根 CA 私钥必须由 root 独占,这是 PKI 安全的底线。中间 CA 的操作,我们通过sudo -u causer切换用户来完成,既保证安全,又不失灵活性。
3. 核心细节解析与实操要点
3.1 根 CA 私钥生成:强度、加密与存储的三重权衡
生成根 CA 私钥是整个 PKI 的第一道也是最重要的一道关卡。它决定了整个信任链的密码学强度。在 CentOS 8 上,我们使用以下命令:
cd /etc/pki/CA openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 \ -aes-256-cbc -out private/cakey.pem这条命令看似简单,但每个参数都有深意:
-algorithm rsa:指定 RSA 算法。虽然 ECDSA(如 prime256v1)密钥更短、性能更好,但 RSA 兼容性最广,Windows、Java、旧版嵌入式设备均无兼容问题;-pkeyopt rsa_keygen_bits:4096:密钥长度设为 4096 位。2048 位在当前算力下仍安全,但 NIST 已建议向 3072 位过渡;4096 位是兼顾安全性与未来十年可用性的保守选择。实测在 CentOS 8 的 Intel Xeon E5-2680 上,生成耗时约 12 秒,完全可接受;-aes-256-cbc:使用 AES-256-CBC 算法加密私钥。这里坚决不用-des3(Triple DES),因为 DES 已被 NIST 淘汰,且-des3在某些 OpenSSL 版本中存在弱密钥漏洞;也不用-aes-128-cbc,因为 128 位密钥在量子计算威胁下防护期较短。AES-256 是当前公认的黄金标准;-out private/cakey.pem:输出路径严格遵循前述目录结构。
执行后,系统会提示输入密码短语(Passphrase)。这个密码必须强且可记忆:建议采用“三个随机单词+一个数字+一个符号”组合,例如HorseBatteryStaple!2024。它不是用来输几百次的,而是每年可能只输 1-2 次(签发新中间 CA 时),所以宁可复杂,绝不能弱。
注意:如果你在生成过程中按 Ctrl+C 中断,
private/cakey.pem文件可能已创建但内容不完整。务必用file private/cakey.pem检查文件类型,确认是 “PEM RSA private key”;若显示 “data”,说明文件损坏,需删除后重试。这是新手最常踩的坑之一。
3.2 根证书自签名:openssl req的七个关键扩展项
有了私钥,下一步是生成自签名根证书。命令如下:
openssl req -x509 -new -nodes -key private/cakey.pem \ -sha256 -days 10950 \ -out certs/ca.crt \ -config openssl.cnf其中-config openssl.cnf指向我们精心编写的配置文件,它才是真正的核心。以下是openssl.cnf中[ CA_default ]和[ req ]段的关键配置及其原理:
[ CA_default ] dir = /etc/pki/CA certs = $dir/certs crl = $dir/crl new_certs_dir = $dir/newcerts database = $dir/index.txt serial = $dir/serial RANDFILE = $dir/private/.rand [ req ] default_bits = 4096 distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca prompt = no [ req_distinguished_name ] C = CN ST = Beijing L = Haidian O = MyCompany Root CA OU = PKI Department CN = MyCompany Root Certificate Authority emailAddress = ca-admin@mycompany.com [ v3_ca ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign逐条解析:
default_bits = 4096:确保 CSR 中的公钥长度与私钥一致,避免openssl ca签发时因密钥不匹配报错;distinguished_name = req_distinguished_name:指定 DN(Distinguished Name)字段来源,prompt = no表示不交互输入,全部从配置中读取,保证自动化;x509_extensions = v3_ca:告诉openssl req在生成自签名证书时,应用[v3_ca]段定义的 X.509 v3 扩展;[v3_ca]中的basicConstraints = critical, CA:true是最核心的一行。它声明此证书是一个 CA 证书,且该扩展是 critical(关键),任何不理解此扩展的客户端必须拒绝该证书。没有它,你的根证书就只是一个普通终端证书,无法签发其他证书;keyUsage = critical, digitalSignature, cRLSign, keyCertSign:定义密钥用途。digitalSignature用于签发证书和 CRL,cRLSign用于签署吊销列表,keyCertSign用于签署其他证书。三者缺一不可;subjectKeyIdentifier = hash和authorityKeyIdentifier = keyid:always,issuer:前者为证书生成唯一 SKID(Subject Key Identifier),后者将签发者的 AKID(Authority Key Identifier)嵌入证书,形成证书链追溯的基础。没有它们,openssl verify无法构建信任链。
执行该命令后,系统会要求输入刚才设置的私钥密码。成功后,certs/ca.crt即为根证书。用openssl x509 -in certs/ca.crt -text -noout | grep -A1 "X509v3"验证,应看到X509v3 Basic Constraints: critical和X509v3 Key Usage: critical, Digital Signature, Certificate Sign, CRL Sign等字段。
3.3 中间 CA 的创建:为什么必须用openssl ca而非openssl req?
中间 CA 不能像根 CA 那样用openssl req -x509自签名,因为它必须由根 CA 签发,以建立信任链。正确的流程是:
- 为中间 CA 生成私钥;
- 用该私钥创建 CSR(Certificate Signing Request);
- 用根 CA 的私钥和证书,通过
openssl ca命令签发中间 CA 证书。
步骤 1 和 2 很简单:
mkdir -p /opt/pki/intermediate/{private,csr,certs,crl,newcerts} chmod 700 /opt/pki/intermediate/private cd /opt/pki/intermediate openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 \ -aes-256-cbc -out private/intermediate.key.pem openssl req -new -key private/intermediate.key.pem \ -out csr/intermediate.csr \ -config /opt/pki/intermediate/openssl.cnf关键在步骤 3。很多人误以为openssl req -x509也能签,但这是错误的:openssl req -x509只能生成自签名证书,它不读取index.txt,不更新serial,不生成AKID/SKID,无法构建可验证的信任链。必须用openssl ca:
cd /etc/pki/CA # 初始化数据库 touch index.txt echo 1000 > serial # 签发中间 CA openssl ca -config openssl.cnf \ -extensions v3_intermediate_ca \ -days 3650 \ -notext \ -md sha256 \ -in /opt/pki/intermediate/csr/intermediate.csr \ -out /opt/pki/intermediate/certs/intermediate.crt \ -keyfile private/cakey.pem \ -cert certs/ca.crt这里-extensions v3_intermediate_ca指向配置文件中专门为中间 CA 设计的扩展段:
[ v3_intermediate_ca ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign与根 CA 的v3_ca段相比,唯一的区别是pathlen:0。它表示此中间 CA不能再签发下一级 CA 证书,只能签发终端实体证书。这是防止信任链无限延伸、提升整体安全性的关键限制。pathlen:0是 NIST SP 800-57 的强制要求。
执行后,openssl ca会提示输入根私钥密码,并在/etc/pki/CA/index.txt中添加一条V(Valid)记录,在/etc/pki/CA/serial中递增序列号。此时,中间 CA 证书已生成,但还不能直接使用——它需要与根证书合并为一个证书链(Certificate Chain),供下游服务(如 Nginx)加载。
3.4 证书链构建与信任锚部署:让客户端真正“信”你
中间 CA 证书intermediate.crt单独存在时,客户端(如浏览器、curl)无法验证其有效性,因为它不知道谁签发了它。解决方案是将中间 CA 证书和根 CA 证书按顺序拼接,形成一个证书链文件:
cat /opt/pki/intermediate/certs/intermediate.crt \ /etc/pki/CA/certs/ca.crt \ > /opt/pki/intermediate/certs/ca-chain.crt顺序必须是:中间证书在前,根证书在后。因为验证时,客户端从终端证书开始,向上寻找签发者,直到找到一个它信任的根证书。如果顺序颠倒,openssl verify会报unable to get issuer certificate。
接下来,是让客户端“信任”这个根。在 CentOS 8 上,标准做法是将根证书ca.crt添加到系统的信任库:
cp /etc/pki/CA/certs/ca.crt /etc/pki/ca-trust/source/anchors/mycompany-root-ca.crt update-ca-trust extractupdate-ca-trust extract命令会将所有anchors/目录下的证书,合并到/etc/pki/tls/certs/ca-bundle.crt中,并重建哈希链接。此后,curl --cacert /opt/pki/intermediate/certs/ca-chain.crt https://test-server就能成功验证,而curl https://test-server(使用系统默认信任库)也会成功,前提是test-server的证书是由该中间 CA 签发的。
实操心得:不要用
cp ca.crt /etc/ssl/certs/ && c_rehash。CentOS 8 的c_rehash工具已被弃用,update-ca-trust是官方推荐的、符合 FHS 标准的方式。我曾在一个客户环境里用c_rehash,结果dnf update后信任库被重置,导致所有内部服务 HTTPS 访问中断,排查了整整一天。
4. 实操过程与核心环节实现
4.1 为 Nginx 服务器签发终端证书:从 CSR 到部署的全流程
现在,我们以最常见的 Nginx Web 服务器为例,演示如何为其签发一张可用于 HTTPS 的终端证书。整个流程分为五步:准备、生成、签发、验证、部署。
第一步:在 Nginx 服务器上生成密钥和 CSR
登录目标 Nginx 服务器(假设 IP 为192.168.10.100),执行:
mkdir -p /etc/nginx/ssl cd /etc/nginx/ssl # 生成 2048 位私钥(终端证书无需 4096 位,2048 足够且性能更好) openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 \ -out nginx.key # 创建 CSR,注意 CN 必须是服务器的 DNS 名称或 IP openssl req -new -key nginx.key \ -out nginx.csr \ -subj "/C=CN/ST=Beijing/L=Haidian/O=MyCompany/OU=Web Services/CN=web.mycompany.local"-subj参数中的CN(Common Name)至关重要。如果 Nginx 通过域名web.mycompany.local访问,这里就必须填这个域名;如果只通过 IP192.168.10.100访问,则填IP:192.168.10.100。现代浏览器要求 SNI(Server Name Indication),且对 CN 匹配极为严格。
第二步:将 CSR 复制到 CA 服务器并签发
在 CA 服务器上(即我们搭建 PKI 的那台 CentOS 8),执行:
# 创建专用目录存放待签发 CSR mkdir -p /opt/pki/intermediate/csr/pending scp user@192.168.10.100:/etc/nginx/ssl/nginx.csr \ /opt/pki/intermediate/csr/pending/nginx-web.csr # 使用中间 CA 签发 cd /opt/pki/intermediate openssl ca -config openssl.cnf \ -extensions server_cert \ -days 365 \ -notext \ -md sha256 \ -in csr/pending/nginx-web.csr \ -out certs/nginx.crt \ -keyfile private/intermediate.key.pem \ -cert certs/intermediate.crt \ -outdir certs/这里-extensions server_cert指向一个专为服务器证书设计的扩展段:
[ server_cert ] basicConstraints = CA:FALSE nsCertType = server keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer关键点:
basicConstraints = CA:FALSE:明确声明这不是 CA 证书,无法签发其他证书;extendedKeyUsage = serverAuth:声明此证书仅用于 TLS 服务器身份验证,符合 RFC 5280;keyUsage = keyEncipherment:允许用此密钥加密会话密钥(RSA 密钥交换),这是传统 TLS 1.2 的必需项。
第三步:将签发的证书和证书链复制回 Nginx 服务器
# 在 CA 服务器上,构建完整的证书链 cat /opt/pki/intermediate/certs/nginx.crt \ /opt/pki/intermediate/certs/ca-chain.crt \ > /opt/pki/intermediate/certs/nginx-fullchain.crt # 复制到 Nginx 服务器 scp /opt/pki/intermediate/certs/nginx-fullchain.crt \ /opt/pki/intermediate/private/intermediate.key.pem \ user@192.168.10.100:/etc/nginx/ssl/第四步:在 Nginx 上配置 HTTPS 并验证
编辑/etc/nginx/conf.d/ssl.conf:
server { listen 443 ssl http2; server_name web.mycompany.local; ssl_certificate /etc/nginx/ssl/nginx-fullchain.crt; ssl_certificate_key /etc/nginx/ssl/intermediate.key.pem; # 推荐的现代 TLS 配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; location / { root /usr/share/nginx/html; index index.html; } }重启 Nginx:systemctl restart nginx。
第五步:本地验证证书链有效性
在你的开发机(Mac/Linux)上,用 OpenSSL 验证:
# 下载根证书到本地 scp root@ca-server:/etc/pki/CA/certs/ca.crt ./mycompany-root.crt # 验证 nginx.crt 是否由 intermediate.crt 签发,且 intermediate.crt 由 ca.crt 签发 openssl verify -CAfile mycompany-root.crt \ -untrusted <(cat /opt/pki/intermediate/certs/intermediate.crt) \ /opt/pki/intermediate/certs/nginx.crt如果输出nginx.crt: OK,说明整个信任链验证通过。此时,用浏览器访问https://web.mycompany.local,地址栏应显示绿色锁图标,点击可查看证书路径:nginx.crt→intermediate.crt→ca.crt。
4.2 CRL(证书吊销列表)的生成与发布:应对私钥泄露的最后防线
证书不是永远有效的,当服务器私钥意外泄露、员工离职需禁用其客户端证书时,我们必须能及时宣告该证书作废。CRL 就是实现这一目标的标准机制。
在 CentOS 8 上,CRL 的生成非常简单,但发布方式需要谨慎设计。首先,生成 CRL:
# 在 CA 服务器上,为根 CA 生成 CRL(通常很少用,仅占位) cd /etc/pki/CA openssl ca -gencrl -out crl/ca.crl -config openssl.cnf # 为中间 CA 生成 CRL(这才是日常使用的) cd /opt/pki/intermediate openssl ca -gencrl -out crl/intermediate.crl \ -keyfile private/intermediate.key.pem \ -cert certs/intermediate.crt \ -config openssl.cnf生成的intermediate.crl是一个 DER 编码的二进制文件。为了让客户端能获取它,我们需要将其发布到一个可通过 HTTP 访问的路径。绝对不能将 CRL 放在/var/www/html/下让 Apache/Nginx 直接提供,因为 CRL 文件本身需要被签名,且其分发点(CRL Distribution Point)必须在证书中明确声明。
正确的做法是:
- 将
intermediate.crl复制到 Web 服务器的某个目录,例如/var/www/html/crl/intermediate.crl; - 在中间 CA 的
openssl.cnf的[ server_cert ]段中,添加 CDP 扩展:
[ server_cert ] ... crlDistributionPoints = URI:http://crl.mycompany.local/intermediate.crl- 在 DNS 中添加
crl.mycompany.local解析到 Web 服务器 IP; - 在 Web 服务器上配置 Nginx,确保该路径可公开访问,且返回正确的 MIME 类型:
location /crl/ { alias /var/www/html/crl/; add_header Content-Type application/pkix-crl; expires 1h; }这样,当客户端(如 Chrome、Firefox)验证nginx.crt时,会自动从http://crl.mycompany.local/intermediate.crl下载 CRL,并检查该证书的序列号是否在吊销列表中。
注意:CRL 有有效期(
nextUpdate字段),默认是 30 天。你可以通过在openssl.cnf的[ CA_default ]段中添加default_crl_days = 7将其缩短为 7 天,以加快吊销生效速度。但频繁生成 CRL 会增加 CA 服务器负载,需权衡。
4.3 客户端证书认证(mTLS):为 API 网关添加双向信任
PKI 的高级用法是双向 TLS(mTLS),即不仅服务器向客户端证明身份,客户端也必须向服务器出示有效证书。这在微服务间通信、API 网关鉴权中极为常见。
以 Nginx 作为 API 网关为例,启用 mTLS 的配置如下:
server { listen 8443 ssl; server_name api.mycompany.local; ssl_certificate /etc/nginx/ssl/api-fullchain.crt; ssl_certificate_key /etc/nginx/ssl/intermediate.key.pem; # 启用客户端证书验证 ssl_client_certificate /etc/nginx/ssl/ca-chain.crt; ssl_verify_client on; ssl_verify_depth 2; location / { proxy_pass http://backend-service; proxy_set_header SSL_CLIENT_CERT $ssl_client_escaped_cert; } }关键参数:
ssl_client_certificate:指定信任的根证书链,即我们之前构建的ca-chain.crt;ssl_verify_client on:强制要求客户端提供证书;
