TLS 与证书
1. 加密与身份
TLS 同时解决两件事:
- 加密:链路被截获也读不出内容,被篡改也能发现
- 身份:向访客证明「连接的确实是
example.com」,而不是冒充它的中间设备
两者是分开的。任何一对公私钥都能协商出加密通道,自签证书同样可以;证书只负责身份。所以「自签不安全」的准确含义是「没人能确认它是谁」,而不是「流量没有加密」。
2. 证书
证书是一份由 CA 签名的文件,核心内容如下:
| 内容 | 作用 |
|---|---|
| 域名 | 这张证书代表哪些名字,格式见 2.1 |
| 公钥 | 与私钥配对,用于握手 |
| 有效期 | 到期即失效,需要重新签发 |
| 签发者与签名 | 让客户端能从内置的根证书验到它 |
私钥不在证书里,与证书分开保存。谁终止 TLS,私钥就在谁手里:证书可以公开,私钥泄露等于身份被冒用。
验证是一条链:客户端从操作系统或浏览器内置的根证书出发,用签发者的公钥逐级验签,直到这张证书。链上少一环——例如只部署了域名证书、漏掉中间证书——验证就不成立,部分客户端会直接报证书无效。
2.1 Subject Alternative Name
证书里列出域名的字段是 Subject Alternative Name(SAN)。客户端只看这一项,CN 已被忽略,只填 CN 的证书在现代客户端上不会被接受。
匹配规则:
| 规则 | 说明 |
|---|---|
| 逐字匹配 | 访问的域名要与 SAN 中某一项完全一致(大小写不敏感) |
| 通配符 | 只能位于最左一个标签,且只覆盖一级:*.example.com 含 a.example.com,不含 example.com,也不含 a.b.example.com |
| 多个名字 | 一个 SAN 可以列出多个域名,一张证书服务多个站点 |
| IP 地址 | IP 也能写进 SAN,用 IP 直连时客户端校验的是这一项 |
| 非 ASCII | 证书里写 punycode 形式(xn-- 开头) |
名字写死在证书里,所以换域名就意味着重新签发;用 IP 访问只签了域名的证书,同样校验不过。
3. 握手
以 TLS 1.3 为例:
ClientHello:访客报出支持的版本与密码套件、一个随机数(client_random),并附上自己的临时公钥;SNI 与 ALPN 也在这里。整条消息都是明文ServerHello:服务器选定一套密码套件,回一个随机数(server_random)和自己的临时公钥。此刻双方的共享秘密已经成立,握手密钥随之导出- 此后全部加密:
EncryptedExtensions(ALPN 结果等)→Certificate(证书链)→CertificateVerify(用证书私钥对握手记录签名)→Finished - 客户端回
Finished:校验通过后才开始发送应用数据 - 应用数据:用协商出的会话密钥对称加密
几点值得注意:
- 证书链在 1.3 里是密钥成立之后才发出,且已经加密;TLS 1.2 则是证书与
ServerKeyExchange同一趟明文送出 - 明文只剩第一条消息,其中的 SNI 正是节点能在不解密的情况下按域名选路的依据,见 HTTP 虚拟主机
CertificateVerify用私钥签名,把证书与本次握手绑定在一起;只看证书链验不出私钥在谁手里- 默认一次往返完成握手;会话恢复可以 0-RTT,代价是 0-RTT 数据有重放风险
4. 双向认证(mTLS)
默认流程里只有服务器出示证书,访客验证它,这叫单向认证。**双向认证(mTLS)**要求访客也出示证书,服务器同样验它的签名链,并确认它由自己信任的 CA 签发。
在 TLS 1.3 里,服务器发出 CertificateRequest,客户端回应自己的 Certificate 与 CertificateVerify,握手其余部分不变;TLS 1.2 的 CertificateRequest 位置靠前一些,本质相同。
两边的证书通常来自不同地方:服务器证书要能被不特定的客户端信任,由公开 CA 签发;客户端证书一般由自己架的 CA 签发,只把该 CA 的证书交给服务器校验即可。
5. 签发
签发是 CA 用自己的私钥为证书签名。公开 CA 不能凭空相信一个域名,签发前要求申请人先证明自己控制着它,证明方式有几种,区别只在于把证明放在哪里,详见 Let's Encrypt 的挑战类型说明。自签不需要证明,代价是没有任何客户端会信任它,除非手动把这张证书或自建的根证书导入信任列表,所以只适合自用测试。
Lolia 的 AutoTLS 用的是 Let's Encrypt 的 TLS-ALPN-01,并且在 frpc 本地完成签发与续期:验证流量先到达节点,再按域名交给 frpc。
6. 结论
- 加密与身份是两件事,自签能加密但证明不了身份
- 客户端只认 SAN 与信任链,名字对不上或链断了都视为无效
- TLS 1.3 先用临时公钥成立密钥,证书在加密之后才送达
- 证书与私钥成对,谁终止 TLS 谁持有私钥
- 签发的是域名而不是机器,域名变了就要重签
- 双向认证让访客也出示证书,但它只证明身份,授权仍在应用层
- 签发发生在本地,验证流量要能经节点到达 frpc