什么是双向 TLS 认证#
普通 HTTPS 连接中,客户端验证服务器证书,确保连接的是真实的服务器。双向 TLS(Mutual TLS,简称 mTLS) 在此基础上增加了服务器对客户端的验证——客户端必须提供有效的证书才能建立连接。
这种认证方式比用户名密码更安全,广泛用于:
- API 服务间的身份认证
- 零信任网络架构
- 企业内部系统访问控制
- 高安全要求的金融、医疗系统
工作原理#
mTLS 握手过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
客户端 服务器
| |
|---------- ClientHello --------------->|
|<--------- ServerHello ----------------|
|<--------- Certificate ----------------| (服务器证书)
|<--------- CertificateRequest ---------| (请求客户端证书)
|<--------- ServerHelloDone ------------|
| |
|---------- Certificate ---------------->| (客户端证书)
|---------- ClientKeyExchange --------->|
|---------- CertificateVerify --------->| (证明拥有私钥)
|---------- Finished ------------------>|
|<--------- Finished -------------------|
| |
|====== 加密通信通道 ====================|
|
关键区别:服务器发送 CertificateRequest,客户端响应自己的证书并用私钥签名证明身份。
实战:使用 OpenSSL 创建 mTLS 环境#
1. 创建 CA 证书#
首先创建一个私有 CA,用于签发服务器和客户端证书:
1
2
3
4
5
6
|
# 生成 CA 私钥
openssl genrsa -out ca.key 2048
# 生成 CA 证书
openssl req -new -x509 -days 365 -key ca.key -out ca.crt \
-subj "/CN=My Private CA/O=My Organization"
|
2. 签发服务器证书#
1
2
3
4
5
6
7
8
9
10
11
|
# 生成服务器私钥
openssl genrsa -out server.key 2048
# 生成 CSR(证书签名请求)
openssl req -new -key server.key -out server.csr \
-subj "/CN=api.example.com/O=My Server"
# 用 CA 签发服务器证书
openssl x509 -req -days 365 -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt
|
3. 签发客户端证书#
1
2
3
4
5
6
7
8
9
10
11
|
# 生成客户端私钥
openssl genrsa -out client.key 2048
# 生成客户端 CSR
openssl req -new -key client.key -out client.csr \
-subj "/CN=alice/O=My Client"
# 用 CA 签发客户端证书
openssl x509 -req -days 365 -in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt
|
4. 验证证书链#
1
2
3
4
5
6
7
|
# 验证服务端证书
openssl verify -CAfile ca.crt server.crt
# 输出: server.crt: OK
# 验证客户端证书
openssl verify -CAfile ca.crt client.crt
# 输出: client.crt: OK
|
5. 测试 mTLS 连接#
使用 OpenSSL 内置的测试服务器:
1
2
3
4
|
# 终端 1:启动 mTLS 服务器
openssl s_server -accept 8443 \
-cert server.crt -key server.key \
-CAfile ca.crt -Verify 1 -www
|
-Verify 1 表示要求客户端证书,验证深度为 1。
1
2
3
4
|
# 终端 2:客户端连接(使用证书)
openssl s_client -connect localhost:8443 \
-cert client.crt -key client.key \
-CAfile ca.crt
|
成功连接后,你会看到证书链信息和 Verify return code: 0 (ok)。
Nginx 配置 mTLS#
在 Nginx 中启用客户端证书验证:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
server {
listen 443 ssl;
server_name api.example.com;
# 服务端证书
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# 客户端证书验证
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
location / {
# 可从证书中提取客户端信息
proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
proxy_pass http://backend;
}
}
|
配置说明:
ssl_client_certificate:指定信任的 CA 证书
ssl_verify_client on:强制要求客户端证书
ssl_verify_depth:证书链验证深度
为客户端生成 PKCS#12 证书#
浏览器和操作系统通常需要 PKCS#12 格式(.p12/.pfx):
1
2
3
|
openssl pkcs12 -export -out client.p12 \
-inkey client.key -in client.crt \
-certfile ca.crt -passout pass:yourpassword
|
客户端双击 .p12 文件导入到系统证书存储,访问时浏览器会自动提示选择证书。
常见问题排查#
客户端证书不被接受#
检查证书用途:
1
2
|
openssl x509 -in client.crt -noout -purpose | grep "SSL client"
# 应输出: SSL client : Yes
|
证书链验证失败#
确保 CA 证书正确:
1
2
3
4
5
|
# 查看证书签发者
openssl x509 -in client.crt -noout -issuer
# 查看CA证书主题
openssl x509 -in ca.crt -noout -subject
# 两者应匹配
|
连接被拒绝#
确认服务器开启了客户端验证:
1
2
|
openssl s_client -connect api.example.com:443 -showcerts
# 查看 ServerHello 中是否包含 CertificateRequest
|
安全建议#
- 使用独立的 CA:客户端证书 CA 应与服务器证书 CA 分离
- 设置合理的有效期:客户端证书有效期不宜过长
- 支持证书吊销:配置 CRL 或 OCSP 实现证书即时吊销
- 保护客户端私钥:客户端私钥应加密存储,或使用硬件令牌
- 日志审计:记录证书认证日志,便于安全审计