SSL 客户端认证就是双向 TLS:客户端也要出示证书,服务端验它的链和签名。 失败发生在握手阶段,连 HTTP 都还没开始。
前两节都在「知识」这一类里打转。这一节换成「持有」——凭据不是记住的,是放在设备里的。
一、四步握手
- 客户端先验证服务端证书(和普通 HTTPS 一样,这一步没变)。
- 服务端回一个
CertificateRequest,列出它信任的 CA。 - 客户端挑一张由该 CA 签发的证书发过去,并用对应私钥签一段握手数据。
- 服务端验这张证书的链、验那个签名,两样都过才继续握手。 注意第 2 步是「列出信任哪些 CA」,不是「列出信任哪些人」。 所以服务端不需要预先知道每个用户,只要他们都在同一个 CA 下面就行。
二、失败的层次不一样
Demo 里服务端设了 verify_mode = CERT_REQUIRED,
客户端不带证书时直接 ConnectionResetError——不是 401,是连接被重置。
带上证书的那次通过,收到 135 字节。两次的差别不在应用层,在握手层。
这一点很重要:客户端认证的失败不会走到 HTTP 状态码。
所以排查时看服务端日志会看到 TLS 握手失败,而不是 401;
如果有反向代理,日志可能落在代理而不是后端应用上。
三、适合谁用
适合机器对机器:私钥放在文件里,不需要人记,也不会被钓鱼。 适合高价值操作:银行后台运维、企业内部系统、物联网设备上报——设备天然适合放证书, 不适合让人天天输密码。 不适合面向大众的网站。原因不是技术不行,是分发和换发太麻烦——8.4.3 会用量出来的数字讲这件事。 还有一个细节:客户端证书里的身份是签发时定死的(比如 CN=bozai-client)。 一旦这张证书被复制走,服务端无法区分「本人」和「拿到文件的人」。 所以私钥的保护(比如放 TPM、放 U 盾)才是这套机制的安全边界。
四、动手 Demo:没证书连不上,有证书才谈 HTTP
用 openssl 造一个 CA、一张服务端证书、一张客户端证书;
服务端设 CERT_REQUIRED 并要求客户端出示证书;客户端分别带和不带证书各连一次。
sctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sctx.load_cert_chain(sv_p, sv_k)
sctx.verify_mode = ssl.CERT_REQUIRED # 关键:要求客户端证书
sctx.load_verify_locations(ca_p) # 信任哪个 CA
cctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
cctx.load_verify_locations(ca_p)
cctx.load_cert_chain(cl_p, cl_k) # 客户端出示自己的证书
实跑输出:
--------------------------------------------------------------------
SSL 客户端认证:把证书挂到人身上
--------------------------------------------------------------------
服务端设了 verify_mode=CERT_REQUIRED,也就是「没证书别谈」。
客户端两种姿态各试一次:
不带客户端证书 失败:ConnectionResetError
带客户端证书 通过,收到 135 字节
差别发生在**握手阶段**,不在 HTTP 层。没证书的那次连 401 都收不到——
TLS 握手本身就被服务端打断了,因为它在 ClientHello 之后要求客户端出证书。
认证链条是这样的:
1. 客户端先验证服务端证书(和普通 HTTPS 一样)
2. 服务端回一个 CertificateRequest,列出它信任的 CA
3. 客户端挑一张由该 CA 签发的证书发过去,并用私钥签一段握手数据
4. 服务端验这张证书的链、验那个签名,两样都过才继续
所以客户端认证的强度等于「私钥保管得多好」,跟密码无关。
这也解释了为什么它适合机器对机器:证书放在文件里,不需要人记。
不带证书:ConnectionResetError——握手被服务端打断,连接直接断。
带证书:通过,收到 135 字节。
两次都没走到 401/403。客户端认证的成败在 TLS 层就定了,应用层甚至不知道自己被拒过。
这也是为什么它常和反向代理一起用:证书校验交给代理做,代理把「已认证的用户名」通过首部传给后端。
30 秒实验
- 把客户端证书的 CN 换成别的名字,看服务端能不能察觉(提示:它不检查,只看链)。
- 把
verify_mode改成CERT_OPTIONAL,再试不带证书的情况。 - 想一想:如果私钥文件被复制走了,服务端有什么办法区分吗?
常见误区
- 「客户端认证失败会回 401」——失败在握手层,看不到 HTTP 状态码。
- 「服务端要预先登记每个用户」——只要登记 CA,用户由 CA 签发。
- 「有证书就绝对安全」——证书被复制走就等于身份被复制。
自测三问(点开对答案)
1. SSL 客户端认证在第几步发生? —— 握手阶段,服务端发 `CertificateRequest` 之后。 2. 不带客户端证书会看到什么? —— 握手失败,连接被重置,不会有 HTTP 状态码。 3. 服务端校验客户端证书时看哪两样? —— 证书链能不能追到受信 CA,以及客户端用私钥签的那段数据对不对。单靠证书还不够强。把它和密码叠起来,才是真正的双因素。
- ☐ (本节完,下一节 8.4.2 SSL客户端认证采用双因素认证)