什么是双向 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

安全建议

  1. 使用独立的 CA:客户端证书 CA 应与服务器证书 CA 分离
  2. 设置合理的有效期:客户端证书有效期不宜过长
  3. 支持证书吊销:配置 CRL 或 OCSP 实现证书即时吊销
  4. 保护客户端私钥:客户端私钥应加密存储,或使用硬件令牌
  5. 日志审计:记录证书认证日志,便于安全审计

参考