Email:Service@dogssl.com
CNY
Nginx配置双向SSL认证的实战操作
更新时间:2025-12-01 作者:双向SSL认证

双向SSL认证(简称 mTLS)通过 “服务器验证客户端身份 + 客户端验证服务器身份” 的双向校验机制,构建更安全的通信链路。本文将从双向SSL认证原理切入,详细讲解基于 OpenSSL 生成证书、Nginx 配置双向认证、客户端验证等实战步骤,并针对常见问题提供解决方案,帮助开发者快速落地双向认证防护。

一、双向SSL认证核心原理与应用场景

1. 双向SSL认证与单向 SSL 的区别

单向SSL认证(常规 HTTPS)仅要求客户端验证服务器证书合法性,确保客户端连接的是 “真实服务器”,但服务器无法确认客户端身份,存在非法客户端仿冒风险。而双向SSL认证在此基础上增加 “服务器验证客户端证书” 环节,通信双方需互相校验证书,具体流程如下:

(1)TCP 三次握手:客户端与 Nginx 服务器建立 TCP 连接;

(2)服务器证书验证(单向认证基础):

  • 服务器向客户端发送自身证书(含公钥、颁发机构等信息);
  • 客户端验证服务器证书:检查证书是否由信任的CA签发、有效期是否有效、域名是否匹配;

(3)客户端证书验证(双向认证新增):

  • 客户端验证服务器证书通过后,向服务器发送自身证书;
  • 服务器验证客户端证书:检查证书签发机构、有效期、证书链完整性,确认客户端为授权主体;

(4)密钥协商与加密通信:双方验证通过后,使用证书中的公钥协商会话密钥,后续 HTTP 数据通过对称加密传输。

2. 双向SSL认证的典型应用场景

  • 企业内部系统:如 OA 系统、财务系统,仅允许安装了企业签发证书的设备访问;
  • API 接口防护:第三方服务调用 API 时,需提供客户端证书,防止接口被未授权访问;
  • 金融 / 医疗数据传输:用户登录、交易数据传输等场景,确保通信双方身份真实,避免数据泄露或篡改;
  • 物联网设备通信:设备与云端平台通信时,通过客户端证书验证设备合法性,防止伪造设备接入。

二、实战准备:环境与工具说明

在开始配置前,需确保环境满足以下要求,同时准备好必要工具:

1. 环境要求

  • 服务器系统:CentOS 7/8、Ubuntu 20.04 LTS(本文以 CentOS 7 为例);
  • Nginx 版本:1.16+(需支持 SSL 模块,默认编译时已包含,可通过nginx -V | grep ssl验证,显示 “--with-http_ssl_module” 即为支持);
  • OpenSSL 版本:1.0.2+(系统默认自带,可通过openssl version查看);
  • 客户端:Windows/macOS 电脑(用于生成证书、测试访问)、浏览器(Chrome/Firefox)或 Postman(用于接口测试)。

2. 核心工具说明

  • OpenSSL:用于生成CA根证书、服务器证书、客户端证书(开源加密工具,系统默认安装);
  • Nginx:作为 Web 服务器,配置 SSL 双向认证规则;
  • 文本编辑器:如 Vi/Vim(服务器端编辑配置文件)、Notepad++(本地编辑证书配置文件);
  • 证书管理工具:Windows “证书管理器”、macOS “钥匙串访问”(用于安装客户端证书)。

三、第一步:使用 OpenSSL 生成证书链

双向SSL认证需构建 “CA根证书→服务器证书→客户端证书” 的完整证书链,其中CA根证书为自签(测试环境)或第三方权威CA签发(生产环境),服务器证书和客户端证书需由CA根证书签名,确保可被互相验证。

1. 规划证书目录(避免文件混乱)

首先在 Nginx 服务器上创建统一的证书目录,用于存放所有证书文件:

# 进入Nginx配置目录(默认路径,若自定义安装需调整)
cd /etc/nginx
# 创建证书目录,区分 CA、服务器、客户端证书
mkdir -p ssl/ca ssl/server ssl/client
# 设置目录权限(仅root可读写,防止证书泄露)
chmod -R 700 ssl

2. 生成CA根证书(自签,测试环境用)

CA根证书是证书链的 “信任源头”,服务器和客户端证书需由其签名才能被互相认可。生产环境建议使用 Let's Encrypt、Symantec 等权威CA签发,此处以测试环境自签CA为例:

(1)生成CA私钥(.key 文件)

私钥是CA的核心密钥,需严格保密,生成时需设置密码保护:

# 进入 CA 证书目录
cd ssl/ca
# 生成2048位RSA私钥(-des3启用密码保护,-out指定输出文件)
openssl genrsa -des3 -out ca.key 2048

执行后会提示输入CA私钥密码(如 “Ca@123456”),后续签名证书时需输入该密码,务必牢记。

(2)生成CA根证书请求(.csr 文件)

证书请求文件包含CA的身份信息(如组织名称、域名等),用于生成最终的根证书:

# 生成CSR文件(-new表示新请求,-key指定CA私钥,-out指定输出文件)
openssl req -new -key ca.key -out ca.csr

执行后需按提示输入信息,关键项说明:

  • Country Name:国家代码(如 CN,中国);
  • State or Province Name:省份(如 Beijing);
  • Locality Name:城市(如 Beijing);
  • Organization Name:组织名称(如 MyCompany,测试环境可自定义);
  • Organizational Unit Name:部门名称(如 IT Dept);
  • Common Name:CA名称(如 MyCompanyCA,需与后续证书的签发者匹配);
  • Email Address:邮箱(可选);
  • A challenge password:请求密码(可选,直接回车跳过)。

(3)生成CA根证书(.crt 文件,有效期 10 年)

用CA私钥签名 CSR 文件,生成可用于签发服务器 / 客户端证书的根证书:

# 生成CA根证书(-x509 表示生成自签证书,-days 指定有效期,-in指定CSR文件)
openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

执行后输入之前设置的CA私钥密码,生成ca.crt(CA根证书,需分发给客户端,用于验证服务器证书)。

3. 生成服务器证书(由CA签名)

服务器证书用于 Nginx 服务器身份验证,客户端通过验证该证书确认服务器合法性:

(1)生成服务器私钥(.key 文件)

# 进入服务器证书目录
cd /etc/nginx/ssl/server
# 生成服务器私钥(无需密码保护,避免Nginx启动时手动输入密码)
openssl genrsa -out server.key 2048

(2)生成服务器证书请求(.csr 文件)

# 生成CSR文件(Common Name需与服务器域名/IP一致,否则客户端会提示证书不匹配)
openssl req -new -key server.key -out server.csr

关键项注意:Common Name必须填写 Nginx 服务器的域名(如api.mycompany.com)或公网 IP(如1.2.3.4),其他项可与CA根证书一致。

(3)用CA根证书签名服务器证书(.crt 文件)

# 用CA私钥签名服务器CSR,生成服务器证书(有效期5年)
openssl x509 -req -days 1825 -in server.csr -signkey ../ca/ca.key -CA../ca/ca.crt -CAcreateserial -out server.crt

参数说明:

  • -CA:指定CA根证书路径;
  • -CAcreateserial:自动生成CA序列号文件(ca.srl),用于后续签名其他证书。

4. 生成客户端证书(由CA签名)

客户端证书用于客户端身份验证,Nginx 服务器通过验证该证书确认客户端是否为授权主体:

(1)生成客户端私钥(.key 文件)

# 进入客户端证书目录
cd /etc/nginx/ssl/client
# 生成客户端私钥(可设置密码,客户端使用时需输入)
openssl genrsa -des3 -out client.key 2048

执行后设置客户端私钥密码(如 “Client@123456”),客户端安装证书时需输入。

(2)生成客户端证书请求(.csr 文件)

# 生成客户端CSR文件(Common Name可填写客户端标识,如“Client-001”)
openssl req -new -key client.key -out client.csr

Common Name 建议填写客户端唯一标识(如设备 ID、用户名),便于 Nginx 后续日志审计。

(3)用CA根证书签名客户端证书(.crt 文件)

# 用CA私钥签名客户端CSR,生成客户端证书(有效期3年)
openssl x509 -req -days 1095 -in client.csr -signkey ../ca/ca.key -CA../ca/ca.crt -CAserial ../ca/ca.srl -out client.crt

(4)转换客户端证书格式(便于浏览器 / Postman 使用)

浏览器和 Postman 通常支持.p12(PKCS#12)格式证书(包含私钥和证书),需将client.keyclient.crt合并为.p12文件:

# 转换为PKCS#12格式(-export表示导出,-inkey指定客户端私钥,-in指定客户端证书)
openssl pkcs12 -export -inkey client.key -in client.crt -out client.p12

执行后设置.p12文件密码(如 “ClientP12@123”),后续客户端导入时需输入该密码。

5. 证书文件清单(确认生成完整性)

完成以上步骤后,证书目录下应包含以下文件,若缺失需重新生成:

目录文件名作用权限要求
ssl/caca.keyCA根证书私钥仅 root 可读(400)
ssl/caca.crtCA根证书(客户端需安装)可读(644)
ssl/caca.csrCA证书请求(可删除)可选删除
ssl/caca.srlCA序列号文件可读(644)
ssl/serverserver.key服务器私钥仅 root 可读(400)
ssl/serverserver.crt服务器证书(Nginx 使用)可读(644)
ssl/serverserver.csr服务器证书请求(可删除)可选删除
ssl/clientclient.key客户端私钥(可删除)客户端保存,服务器无需
ssl/clientclient.crt客户端证书(Nginx 使用)可读(644)
ssl/clientclient.p12客户端 PKCS#12 证书客户端保存

四、第二步:Nginx 配置双向SSL认证

1. 备份原有 Nginx 配置(避免配置错误无法恢复)

# 备份默认配置文件(若使用自定义配置,需备份对应文件)
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak

2. 编写双向SSL认证配置(关键步骤)

创建新的 Nginx 站点配置文件(如ssl-mutual.conf),或修改默认配置文件,核心配置包含 “SSL 基础配置” 和 “双向认证强制配置”:

# 编辑Nginx站点配置
vim /etc/nginx/conf.d/ssl-mutual.conf

# 配置内容如下
server {
    # 监听443端口(HTTPS默认端口)
    listen 443 ssl;
    # 服务器域名/IP(需与服务器证书Common Name一致)
    server_name api.mycompany.com;

    # --------------------------
    # 1. SSL基础配置(单向认证基础)
    # --------------------------
    # 服务器证书(公钥)
    ssl_certificate /etc/nginx/ssl/server/server.crt;
    # 服务器私钥
    ssl_certificate_key /etc/nginx/ssl/server/server.key;
    # 支持的SSL协议(禁用不安全的SSLv2/SSLv3)
    ssl_protocols TLSv1.2 TLSv1.3;
    # 加密套件(优先选择安全性高的套件)
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    # 优先使用服务器加密套件
    ssl_prefer_server_ciphers on;
    # SSL会话缓存(提升性能)
    ssl_session_cache shared:SSL:10m;
    # SSL会话超时时间
    ssl_session_timeout 10m;

    # --------------------------
    # 2. 双向认证强制配置(核心)
    # --------------------------
    # 信任的CA根证书(用于验证客户端证书)
    ssl_client_certificate /etc/nginx/ssl/ca/ca.crt;
    # 强制验证客户端证书(on=强制,optional=可选,optional_no_ca=允许无CA证书)
    ssl_verify_client on;
    # 验证客户端证书的深度(0=仅验证客户端证书,1=验证客户端证书+其签发者)
    ssl_verify_depth 1;

    # --------------------------
    # 3. 日志配置(便于审计客户端身份)
    # --------------------------
    # 访问日志:记录客户端证书信息(如序列号、DN)
    access_log /var/log/nginx/ssl-mutual-access.log main '$remote_addr [$time_local] "$request" $status '
                                                        ' "$ssl_client_s_dn" "$ssl_client_serial"';
    # 错误日志
    error_log /var/log/nginx/ssl-mutual-error.log warn;

    # --------------------------
    # 4. 站点根目录与默认页面(示例)
    # --------------------------
    root /usr/share/nginx/html;
    index index.html index.htm;

    # 测试接口(可用于验证双向认证是否生效)
    location /test {
        default_type application/json;
        return 200 '{"status":"success","message":"双向SSL认证通过","client_dn":"$ssl_client_s_dn"}';
    }
}

# 可选:将HTTP请求强制跳转至HTTPS
server {
    listen 80;
    server_name api.mycompany.com;
    # 301永久重定向至HTTPS
    return 301 https://$host$request_uri;
}

3. 配置参数说明(避免踩坑)

(1)ssl_verify_client:核心参数,取值说明:

  • on:强制要求客户端提供证书,无证书或证书验证失败则拒绝访问(生产环境推荐);
  • optional:客户端可提供或不提供证书,提供则验证,不提供则按单向认证处理(适合混合场景);
  • optional_no_ca:允许客户端提供无CA签名的证书(安全性低,不推荐)。

(2)ssl_client_certificate:必须指定CA根证书路径,Nginx 通过该证书验证客户端证书是否由信任的CA签发;

(3)ssl_verify_depth:若客户端证书是 “CA根证书→中间CA→客户端证书” 的多层链,需将该值设为 “中间CA层数 + 1”(如 1 层中间CA则设为 2),测试环境通常为 1;

(4)日志变量:$ssl_client_s_dn记录客户端证书的 “主题 DN”(如组织、用户名),$ssl_client_serial记录客户端证书序列号,便于审计客户端身份。

4. 验证配置文件语法并重启 Nginx

# 验证Nginx配置语法(若报错需检查路径、括号是否匹配)
nginx -t

# 重启Nginx服务(使配置生效)
systemctl restart nginx

# 检查Nginx运行状态(确保启动成功)
systemctl status nginx

若重启失败,可通过journalctl -u nginx -f查看错误日志,常见原因:证书路径错误、私钥权限不足、配置语法错误。

五、第三步:客户端验证双向认证效果

配置完成后,需通过客户端(浏览器、Postman)测试双向认证是否生效,未安装客户端证书或证书无效时,应拒绝访问;安装正确证书后,可正常访问。

1. 客户端准备:安装CA根证书与客户端证书

(1)安装CA根证书(客户端信任服务器证书)

1)Windows:

  • 将服务器的ca.crt文件复制到 Windows 电脑;
  • 双击ca.crt→点击 “安装证书”→选择 “当前用户” 或 “本地计算机”→“将所有证书放入以下存储”→浏览→选择 “受信任的根证书颁发机构”→完成;
  • 验证:打开 “控制面板→Internet 选项→内容→证书→受信任的根证书颁发机构”,确认 “我的公司CA” 存在。

2)macOS:

  • 复制ca.crt到 macOS 电脑,双击打开→点击 “添加”→输入系统密码;
  • 打开 “钥匙串访问→系统→证书”,找到 “我的公司CA”→右键→“显示简介”→“信任”→“使用此证书时” 选择 “始终信任”;
  • 关闭窗口,输入密码确认。

(2)安装客户端证书(.p12 格式,服务器验证客户端)

1)Windows:

  • 复制client.p12到 Windows 电脑,双击打开→“下一步”→输入.p12文件密码(如 “ClientP12@123”)→“下一步”→完成;
  • 验证:“控制面板→Internet 选项→内容→证书→个人”,确认 “Client-001” 证书存在。

2)macOS:

  • 双击client.p12→输入密码→“添加”→输入系统密码;
  • 验证:“钥匙串访问→登录→证书”,确认 “Client-001” 存在。

2. 浏览器测试(Chrome 为例)

(1)打开 Chrome 浏览器,输入 Nginx 服务器地址(如https://api.mycompany.com/test);

(2)首次访问时,浏览器会弹出 “选择证书” 对话框,选择安装的 “Client-001” 证书→点击 “确定”;

(3)若显示{"status":"success","message":"双向SSL认证通过","client_dn":"CN=Client-001, O=MyCompany, L=Beijing, ST=Beijing, C=CN"},说明认证通过;

(4)测试失败场景:

  • 未安装客户端证书:浏览器提示 “400 Bad Request: No required SSL certificate was sent”;
  • 安装无效证书(如其他CA签发的证书):浏览器提示 “400 Bad Request: SSL certificate error”。

3. Postman 测试(API 接口场景)

(1)打开 Postman,创建新的 GET 请求,URL 输入https://api.mycompany.com/test;

(2)配置客户端证书:

  • 点击 Postman 右上角 “设置图标→Settings→Certificates→Add Certificate”;
  • 填写 “Host”(如api.mycompany.com:443);
  • “PFX File” 选择client.p12文件,“Passphrase” 输入.p12密码→“Add”;

(3)发送请求,若响应状态码为 200,且返回成功信息,说明认证通过;

(4)未配置证书时,发送请求会返回 “400 Bad Request”,与浏览器测试一致。

4. 服务器日志验证(审计客户端身份)

客户端访问后,可通过 Nginx 访问日志查看客户端证书信息,确认身份是否正确:

# 查看访问日志
tail -f /var/log/nginx/ssl-mutual-access.log

日志中会包含客户端证书的 DN 和序列号,示例:

192.168.1.100 [01/Dec/2025:15:30:45 +0800] "GET /test HTTP/1.1" 200  "CN=Client-001, O=MyCompany, L=Beijing, ST=Beijing, C=CN" "01"

六、常见问题与解决方案

1. Nginx 启动报错:“SSL_CTX_use_PrivateKey_file failed”

原因:服务器私钥(server.key)权限过高(如 755),或路径错误;

解决方案:

# 修正私钥权限(仅root可读)
chmod 400 /etc/nginx/ssl/server/server.key
# 确认私钥路径与配置文件一致
ls -l /etc/nginx/ssl/server/server.key

2. 客户端访问提示:“NET::ERR_CERT_COMMON_NAME_INVALID”

原因:服务器证书的 Common Name 与访问的域名 / IP 不一致;

解决方案:重新生成服务器证书,确保openssl req时 “Common Name” 填写正确的域名或 IP。

3. 客户端访问提示:“400 Bad Request: SSL certificate error”

原因:客户端证书不是由 Nginx 配置的CA根证书(ssl_client_certificate)签发,或客户端证书已过期;

解决方案:

  • 确认客户端证书是由ca.crt签名的client.crt
  • 检查客户端证书有效期:openssl x509 -in client.crt -noout -dates
  • 重新生成客户端证书并安装。

4. Nginx 日志显示:“ssl_client_verify: 18, depth 0”

原因:ssl_verify_depth配置值过小,无法验证客户端证书的完整链(如存在中间CA);

解决方案:ssl_verify_depth设为 “中间CA层数 + 1”,如存在 1 层中间CA则设为 2。

5. 客户端证书安装后,浏览器未弹出 “选择证书” 对话框

原因:浏览器缓存了旧的 SSL 会话,或证书未正确安装;

解决方案:

  • 清除浏览器 SSL 缓存(Chrome:设置→隐私和安全→清除浏览数据→勾选 “网站数据”→清除);
  • 重新安装客户端证书,确认证书在 “个人” 证书目录下。

七、生产环境优化建议

  • 使用权威CA证书:测试环境可用自签CA,生产环境需使用 Let's Encrypt(免费)、DigiCert 等权威CA签发证书,避免客户端提示 “不受信任的证书”;
  • 定期轮换证书:设置证书有效期(如服务器证书 1 年,客户端证书 6 个月),到期前重新生成并替换,避免证书过期导致服务中断;
  • 加强私钥保护:CA私钥(ca.key)应离线存储(如 U 盘),避免放在服务器上;服务器私钥(server.key)可使用ssl_password_file配置密码文件,避免明文密码泄露;
  • 限制客户端证书权限:通过 Nginx 的if ($ssl_client_s_dn !~* "CN=Client-001") { return 403; }配置,仅允许特定客户端证书访问;
  • 启用 OCSP stapling:减少客户端验证证书的时间,配置ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/nginx/ssl/ca/ca.crt;

通过本文的步骤,开发者可完成 Nginx 双向SSL认证的从证书生成到客户端验证的全流程配置。在实际应用中,需结合业务场景调整配置(如是否强制验证客户端证书、日志审计需求),同时注重证书的生命周期管理,确保通信链路的安全性与稳定性。


Dogssl.com拥有20年网络安全服务经验,提供构涵盖国际CA机构SectigoDigicertGeoTrustGlobalSign,以及国内CA机构CFCA沃通vTrus上海CA等数十个SSL证书品牌。全程技术支持及免费部署服务,如您有SSL证书需求,欢迎联系!
相关文档
立即加入,让您的品牌更加安全可靠!
申请SSL证书
0.113699s