openssl req -x509 生成自签名证书,用 openssl s_client 分析 HTTPS 连接的证书链与握手过程,理解 TLS 1.3 相比 TLS 1.2 的握手精简(1-RTT vs 2-RTT),并掌握证书吊销机制(CRL/OCSP)如何防御中间人攻击。前两篇你学了三件密码学工具:对称加密(AES)负责快速加密数据,非对称加密(RSA)负责安全交换密钥,哈希(SHA-256)负责验证完整性。但还差一个关键问题:当浏览器连接 https://example.com 时,怎么确认服务器真的是 example.com 而不是冒充者?
这就需要数字证书(Digital Certificate)。数字证书就像网络世界的护照——它把一个公钥和一个身份信息(域名、组织名)绑定在一起,并由一个可信的第三方机构(CA)签名背书。你出示护照证明"我是谁",海关查验签发机关的印章确认护照不是伪造的;服务器出示证书证明"我的公钥属于这个域名",浏览器查验 CA 的签名确认证书没被篡改。
当前广泛使用的证书标准是 X.509 v3,定义了证书的具体格式。一张 X.509 证书包含以下核心字段:
| 字段 | 说明 | 示例 |
|---|---|---|
| Version | 证书版本号 | v3 |
| Serial Number | CA 分配的唯一序列号 | 0x0A3F... |
| Issuer | 签发者(CA 的名称) | DigiCert Inc |
| Validity | 有效期(起止时间) | 2026-01-01 ~ 2027-01-01 |
| Subject | 持有者(域名/组织) | example.com |
| Public Key | 持有者的公钥 | RSA 2048 位 |
| Signature | CA 对证书内容的签名 | SHA-256 with RSA |
注意证书的核心逻辑:公钥字段把证书和密码学绑定(有了公钥就能做非对称加密),Subject 字段把证书和身份绑定(声明这个公钥属于谁),Signature 字段把证书和 CA 绑定(证明一个可信机构认可了这种绑定关系)。三者缺一不可——没有签名,任何人都能伪造证书。
CN=example.com, O=Example Inc, C=US。浏览器验证证书时主要检查 CN 或 SAN(Subject Alternative Name)是否与访问的域名匹配。你现在已经知道证书需要 CA 签名才能被信任。但 CA 本身又是谁签名的?答案是:根 CA 自签。根 CA 证书的 Issuer 和 Subject 相同——它自己给自己签名。这听起来像循环论证,但信任的起点本就是"预设信任":操作系统和浏览器内置了一份受信任根 CA 列表(Trust Store),这份列表就是整个 PKI 体系的信任根基。
实际部署中,CA 机构不会直接用根证书签发网站证书——一旦根证书泄露,整个信任体系崩塌。取而代之的是信任链架构:根 CA 签发中间 CA 证书,中间 CA 再签发终端证书。验证时从终端证书逐级向上追溯,直到到达操作系统内置的根证书。
验证链条的逻辑是:用中间 CA 的公钥验证终端证书的签名 → 用根 CA 的公钥验证中间 CA 的签名 → 根 CA 在系统信任列表中,验证通过。每一步都用到上一篇学的哈希和签名原理——CA 对证书内容计算 SHA-256 哈希,然后用 RSA 私钥签名;验证方用 CA 的公钥解密签名,比对哈希值是否一致。
你的 Kali 系统中,受信任根证书列表存放在 /etc/ssl/certs/ 目录下。可以查看其中有多少个根 CA:
可以看到 DigiCert Global Root CA 的 subject 和 issuer 完全相同——这就是自签名根证书的特征。你的系统信任了 600 多个这样的根 CA,每个根 CA 下面可能有多层中间 CA,最终签发数以亿计的网站证书。
理解了证书结构和信任链,现在动手生成一张自己的证书。在本地开发或内网环境中,你无法让公共 CA 给 localhost 签证书——这时候就需要自签名证书(self-signed certificate):自己充当 CA,自己给自己签发。
用 openssl req -x509 一条命令生成私钥和自签名证书:
生成了两个文件:key.pem 是私钥(必须保密),cert.pem 是证书(可以公开)。用 openssl x509 -text 解析证书内容,验证前面讲的字段是否都出现了:
输出中可以看到 Version=3、Signature Algorithm=sha256WithRSAEncryption(上一篇学的哈希+非对称加密组合)、Issuer 和 Subject 完全相同(自签名特征)、Validity 有效期 365 天、公钥为 RSA 2048 位——全部对应第一章讲的字段结构。
-x509 时输出的是 CSR(证书签名请求),不是证书
openssl req -x509 -newkey rsa:2048 ... — -x509 标志直接输出自签名证书,CSR 是提交给 CA 申请签名的中间文件
前三章你掌握了证书的生成与验证。现在把这些知识放入 TLS 握手的完整流程中——这是 HTTPS 安全通信的启动仪式。TLS 握手的目标是:让客户端和服务器在不可信的网络中协商出一个对称密钥,同时验证服务器身份。整个过程中用到了本系列前三篇的全部知识:非对称加密交换密钥、哈希验证完整性、证书验证身份。
TLS 1.2 和 TLS 1.3 的握手流程有显著差异。TLS 1.3 于 2018 年发布(RFC 8446),简化了握手流程、删除了不安全的算法,是当前推荐版本:
| 特性 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手往返 | 2-RTT | 1-RTT(支持 0-RTT 恢复) |
| 密钥交换 | RSA 或 ECDHE | 仅 ECDHE(前向保密) |
| 证书加密 | 明文传输 | 加密传输 |
| 支持的对称算法 | CBC、GCM、ChaCha20 | 仅 AEAD(GCM、ChaCha20) |
TLS 1.2 握手需要 2 个往返(4 个消息来回),流程如下:
TLS 1.3 把这个流程压缩到 1 个往返,并且在 ServerHello 之后就开始加密——连证书都是在加密通道中传输的,窃听者无法看到你访问了哪个网站的证书:
TLS 1.3 还引入了 0-RTT 恢复模式:如果客户端之前连接过这个服务器(有 PSK——预共享密钥),可以在第一个包里就携带加密的应用数据,实现"零延迟"恢复连接。代价是存在重放攻击风险,因此只适用于幂等请求(如 GET)。
理论掌握了,现在用 openssl s_client 实际分析一个 HTTPS 连接。这是渗透测试和运维排障中最常用的 TLS 诊断工具——它能展示握手协商的协议版本、密码套件、证书链和验证结果。
输出中可以看到:协商使用了 TLS 1.3 和 AES-256-GCM 密码套件(上一篇讲的 GCM 分组模式),证书链从 example.com(终端)到 DigiCert TLS RSA SHA256 2020 CA1(中间 CA)再到 DigiCert Global Root CA(根 CA),验证返回码 0 表示信任链完整。
-servername 参数指定 SNI(Server Name Indication)——它告诉服务器客户端要访问哪个域名。一台服务器可能托管多个域名的证书,没有 SNI 服务器不知道该返回哪张证书。在 TLS 1.3 中 SNI 本身也是明文的(ServerHello 之前的数据都明文),这也是 ECH(Encrypted Client Hello)扩展要解决的问题。
证书有有效期,但在有效期内也可能需要"作废"——比如私钥泄露、域名转手、或 CA 被入侵签发了伪造证书。PKI 提供了两种证书吊销机制:CRL(证书吊销列表)和 OCSP(在线证书状态协议)。
| 机制 | 原理 | 缺点 |
|---|---|---|
| CRL | CA 定期发布已吊销证书的列表,客户端下载并检查 | 列表越来越大、更新不及时 |
| OCSP | 客户端实时向 CA 查询某张证书是否有效 | 泄露用户访问了哪些网站(隐私问题) |
| OCSP Stapling | 服务器代替客户端获取 OCSP 响应并在握手中附带 | 解决隐私+性能问题(推荐方案) |
如果证书验证失败——比如证书过期、域名不匹配、或信任链断裂——浏览器会显示"您的连接不是私密连接"警告。渗透测试中常见的中间人攻击(MITM)正是利用伪造证书来截获 HTTPS 流量:
攻击者在受害者和服务器之间转发流量,向受害者出示一张伪造的证书。如果受害者不检查证书警告直接点击"继续访问",攻击者就能解密所有 HTTPS 流量。防御手段包括:浏览器对 DV 证书警告越来越严格(HTTP Strict Transport Security 强制 HTTPS)、证书透明度日志(Certificate Transparency)让伪造证书可被发现、以及 CT log 监控服务。
渗透测试中,Burp Suite 和 mitmproxy 工具正是利用这个原理——它们生成一张自签名 CA 证书并安装到测试设备上,然后作为中间人代理截获所有 HTTPS 流量。这在授权测试中是合法的调试手段,但如果设备上出现了你不知道的 CA 证书,就要警惕是否被植入了中间人代理。
ls /usr/local/share/ca-certificates/ 应该为空或只包含你自己添加的证书。用 dpkg-reconfigure ca-certificates 可以管理系统信任的根证书列表。模块0-3的三篇文章构成了完整的密码学知识体系。从对称加密到非对称加密,从哈希到证书,最终组装成 HTTPS 底层的 TLS 协议。回顾一下三个核心问题和对应的密码学工具:
| 安全问题 | 密码学工具 | 文章 |
|---|---|---|
| 数据机密性 | AES 对称加密 + RSA 非对称加密 | 第11篇 |
| 数据完整性 + 身份认证 | SHA-256 哈希 + HMAC + bcrypt 密码哈希 | 第12篇 |
| 身份验证 + 密钥协商 | X.509 证书 + CA 信任链 + TLS 握手 | 第13篇 |
这三篇合在一起回答了一个核心问题:HTTPS 如何在不可信的互联网上实现安全通信。答案就是 TLS 握手时用证书验证身份(第13篇)、用 ECDHE 交换密钥(第11篇的 Diffie-Hellman)、用 SHA-256 验证握手完整性(第12篇),然后用 AES-GCM 加密所有后续数据(第11篇的对称加密)。至此,密码学基础模块完结。
openssl s_client -connect baidu.com:443 -servername baidu.com 连接百度,确认协商的 TLS 版本和密码套件,查看证书链有几层。TLSv1.3、TLS_AES_256_GCM_SHA384。证书链通常两层:baidu.com 终端证书 + GlobalSign 根 CA(或中间 CA)。openssl s_server 在 8443 端口启动 HTTPS 服务,然后用 curl -k https://localhost:8443 访问。接着不加 -k 再试一次,观察错误信息。openssl s_server -accept 8443 -cert cert.pem -key key.pem -www。不加 -k 时 curl 报 self-signed certificate 错误——这正是自签名证书不被系统信任的表现。openssl s_client 验证证书链和协议版本。如果没有公网域名,用内网自建 CA 模拟:生成根 CA → 签发服务器证书 → 安装根 CA 到系统信任列表。openssl genrsa -out ca.key 4096 生成 CA 私钥,openssl req -x509 -new -key ca.key -out ca.crt -days 3650 生成自签名 CA 证书,然后生成服务器 CSR 并用 CA 签名:openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt。把 ca.crt 复制到 /usr/local/share/ca-certificates/ 并运行 update-ca-certificates。