本文目录导读:

双向证书认证(mTLS)是一种比单向 TLS 更安全的身份验证机制,要求客户端和服务器各自出示证书来证明身份,开启方式取决于你使用的软件、服务或编程框架,以下是几种常见场景的详细操作指南:
核心原理与通用步骤
在开始之前,理解双向认证的通用流程很重要,无论用哪种中间件,本质都是配置以下三部分:
- 信任链(CA 证书):双方都需要持有并信任一个共同的根证书颁发机构(CA)或彼此直接信任对方的颁发者。
- 服务端证书与私钥:服务器向客户端证明自己是服务端。
- 开启客户端证书验证:服务器主动要求客户端提供证书,并验证其有效性。
主流工具/平台开启方式
使用 Nginx(最常见的 Web 服务器/反向代理)
Nginx 通过 server 块中的几个指令来开启,你需要先准备好 CA 证书、服务器证书和私钥。
配置示例(在 nginx.conf 的 server 块中):
server {
listen 443 ssl;
server_name yourdomain.com;
# 服务器证书与私钥(服务端证明自己)
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
# 开启客户端证书验证
ssl_verify_client on; # 开启双向认证
ssl_client_certificate /etc/ssl/ca.crt; # 指定信任的CA证书(用于验证客户端证书)
ssl_verify_depth 2; # 可选:证书链验证深度,一般默认1或2
# 可选: 将客户端证书信息传递给后端(如 User-CN)
ssl_client_certificate /etc/ssl/ca.crt;
proxy_set_header X-SSL-Client-Cert $ssl_client_raw_cert;
proxy_set_header X-SSL-Client-Subject $ssl_client_s_subject;
location / {
proxy_pass http://backend;
# 其他配置...
}
}
验证:
- 重启 Nginx:
nginx -s reload - 用 curl 测试(需提供客户端证书与私钥):
curl -k --cert client.crt --key client.key https://yourdomain.com
- 如果不提供客户端证书,会收到
400 Bad Request(No required SSL certificate was sent)。
使用 Apache HTTP Server
在 Apache 中通过 SSLVerifyClient 指令配置。
配置示例(在虚拟主机或目录配置中):
<VirtualHost *:443>
ServerName yourdomain.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key
# 启用双向认证
SSLVerifyClient require
SSLVerifyDepth 10
SSLCACertificateFile /etc/ssl/ca.crt
# 可选:将客户端证书 DN 传递给后端变量
RequestHeader set X-SSL-Client-DN "%{SSL_CLIENT_S_DN}s"
DocumentRoot /var/www/html
</VirtualHost>
关键点:
SSLVerifyClient require:必须提供客户端证书。SSLVerifyDepth:证书链验证深度。SSLCACertificateFile:指定用于验证客户端证书的 CA 文件(可以是多个 CA 的拼接文件)。
使用 Tomcat(或 Spring Boot 内嵌 Tomcat)
通过修改 server.xml 中的 Connector 配置。
配置示例(server.xml 的 Connector 部分):
<Connector port="8443" protocol="HTTP/1.1"
maxThreads="150" SSLEnabled="true"
scheme="https" secure="true"
clientAuth="true" <!-- 关键:设置为 "true" 开启双向认证 -->
sslProtocol="TLS"
keystoreFile="/opt/tomcat/conf/server.keystore" <!-- 服务器证书与私钥 -->
keystorePass="password"
truststoreFile="/opt/tomcat/conf/truststore" <!-- 存放受信任的 CA 证书 -->
truststorePass="password" />
参数说明:
clientAuth="true":服务端要求客户端提供证书(want表示可选,false表示不要求)。truststoreFile:用于验证客户端证书的信任库(必须包含客户端证书的颁发者 CA)。keystoreFile:服务端自己的证书与私钥,用于 HTTPS 连接。
使用编程语言框架(如 Go、Java、Python)
示例:Go 的 net/http 实现一个简单的双向认证服务端:
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"net/http"
)
func main() {
// 1. 加载服务器证书与私钥
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
// 2. 加载 CA 证书(用于验证客户端证书)
caCert, err := ioutil.ReadFile("ca.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// 3. 配置 TLS
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert, // 必须验证客户端证书
ClientCAs: caCertPool,
}
server := &http.Server{
Addr: ":443",
TLSConfig: tlsConfig,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 读取客户端证书信息(如果有)
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
certInfo := r.TLS.PeerCertificates[0].Subject.CommonName
w.Write([]byte("Hello, " + certInfo + " (cert authenticated)"))
} else {
w.WriteHeader(http.StatusUnauthorized)
}
}),
}
log.Fatal(server.ListenAndServeTLS("", "")) // 证书已通过 TLSConfig 指定
}
关键注意事项(避免踩坑)
- 证书格式与路径:确保证书为 PEM 格式(
.crt/.pem),私钥不加密(或正确配置密码)。 - 证书链:如果客户端证书由中间 CA 签发,
ssl_client_certificate(Nginx)或truststore要包含完整的 CA 链(根 CA + 中间 CA)。 - 验证深度:默认验证深度可能是 1,如果证书链包含多级,需增加
ssl_verify_depth(Nginx)或SSLVerifyDepth(Apache)。 - 客户端证书吊销:如果安全要求高,建议配置 OCSP 或 CRL(Online Certificate Status Protocol / Certificate Revocation List)吊销检查。
- 性能影响:开启双向认证会增加一次证书交换和签名验证,对高并发场景有轻微性能影响,但通常可忽略。
- 向后兼容:如果只是部分 API 需要双向认证,建议使用两个端口(一个普通 HTTPS,一个双向认证端口),或通过路径/Header 进行区分,而不是全局强制。
快速测试是否开启成功
可以用以下 curl 命令 快速检测你的服务是否真的要求客户端证书:
# 不带客户端证书,预期返回 4xx 错误 curl -v https://yourdomain.com # 带客户端证书与私钥,预期返回正常响应 curl -v --cert client.crt --key client.key https://yourdomain.com
如果第一个命令返回 400 Bad Request(Nginx)或 403 Forbidden(Apache),而第二个命令成功,说明双向认证已生效。
| 工具/平台 | 关键配置项 | 核心指令/参数 |
|---|---|---|
| Nginx | ssl_verify_client on + ssl_client_certificate |
on / optional / off |
| Apache | SSLVerifyClient require + SSLCACertificateFile |
require / optional / none |
| Tomcat | clientAuth="true" + truststoreFile |
true / want / false |
| Go 编程 | tls.Config{ClientAuth: tls.RequireAndVerifyClientCert} |
RequireAndVerifyClientCert / |
开启双向证书认证本质是将服务器的“信任”从单向(只验证服务器)改为双向(双方互验),配置时务必确保服务器信任的 CA 与客户端证书的签发 CA 一致,否则会出现认证失败,如果遇到证书配置正确但仍然不能连接,首先检查证书链的完整性和服务器时间同步(证书有效期)。