41人参与 • 2026-08-29 • SSL
最近在部署一个内部api网关时,遇到了一个典型的需求:外部服务调用我们的接口,部分场景要求高安全性,不仅需要加密传输,还要验证调用方的身份。这让我重新梳理了一遍ssl/tls证书的配置,特别是单向认证和双向认证的区别与实现。很多朋友在初次接触 https 时,往往止步于“让浏览器不显示不安全提示”,但对于后端服务间的通信,尤其是金融、物联网或内部微服务调用,双向认证才是构建可信通信链的关键。这次,我就以最常用的nginx为例,把手动生成证书、配置单向/双向认证,以及其中容易踩坑的细节完整走一遍。
你会发现,所谓的“认证”,核心就是一套基于公钥密码学的“信任”验证机制。单向认证是服务器向客户端证明“我是我”,而双向认证则是客户端和服务器互相证明“你是你”。理解了这个本质,无论是用openssl命令行生成证书,还是配置nginx的 ssl_ 系列指令,都会清晰很多。本文不会停留在“复制粘贴配置就能用”的层面,我会带你理解每一个参数背后的意义,以及在不同生产环境(如云服务器、容器化部署)下的适配要点。
在动手配置之前,我们得先搞清楚几个核心概念,否则配置文件的每一行都会是黑盒操作。
你可以把数字证书想象成一张由权威机构(ca)颁发的“网络身份证”。这张身份证上至少包含以下信息:持有者(服务器或客户端)的名称、持有者的公钥、颁发者(ca)的名称、有效期以及ca的 数字签名 。
当客户端(如浏览器)访问你的 https 站点时,它会收到你的服务器证书。客户端会做两件事:1. 用证书里ca的公钥(来自客户端信任的根证书/中间证书链)去验证证书上ca签名的真实性。2. 检查证书中的域名是否与实际访问的域名一致、是否在有效期内。全部通过,才认为服务器可信。
这是本文的重点,两者的流程差异决定了配置的复杂度。
单向认证 (one-way ssl/tls authentication) : 这是最常见的 https 网站模式。只有客户端验证服务器身份。
双向认证 (two-way ssl/tls authentication / mutual authentication) : 在api网关、银行网银客户端、物联网设备接入等场景常见。客户端和服务器互相验证身份。
简单说,单向认证是“客户查服务器的身份证”,双向认证是“客户和服务器互相查身份证”。
在生产环境,服务器证书通常从受信的商业ca(如digicert, let‘s encrypt)或企业内私有ca获取。但为了学习和测试,我们可以自己扮演ca,用openssl生成全套证书。这能让你透彻理解整个链条。
注意:自签名ca颁发的证书在互联网上不会被公共浏览器默认信任,仅用于内部测试或开发环境。
首先,我们创建一个自己的根ca。
# 1. 创建用于存放ca文件的目录结构 mkdir -p myca/private myca/certs myca/newcerts cd myca touch index.txt echo 1000 > serial # 2. 生成根ca的私钥(建议使用强密码保护,这里为演示使用-nodes去除密码) openssl genrsa -out private/ca.key 2048 # 3. 生成根ca的自签名证书 openssl req -new -x509 -days 3650 -key private/ca.key -out certs/ca.crt \ -subj "/c=cn/st=beijing/l=beijing/o=mytestorg/cn=mytestrootca"
参数解释 :
现在, certs/ca.crt 就是我们的根证书,需要将其导入到需要信任该ca的客户端或服务器系统中。
现在用我们自己的ca为服务器 api.mytest.com 签发证书。
# 1. 生成服务器私钥 openssl genrsa -out server.key 2048 # 2. 生成证书签名请求(csr) openssl req -new -key server.key -out server.csr \ -subj "/c=cn/st=beijing/l=beijing/o=mytestorg/cn=api.mytest.com" # 3. 使用根ca为csr签名,生成服务器证书 openssl ca -in server.csr -out server.crt -cert certs/ca.crt -keyfile private/ca.key -days 365
执行第3步时,openssl会交互式地让你确认签发,两次输入 y 即可。生成的 server.crt 和 server.key 就是用于nginx配置的服务器证书和私钥。
流程与签发服务器证书几乎一样,只是主题信息不同。
# 1. 生成客户端私钥 openssl genrsa -out client.key 2048 # 2. 生成客户端csr openssl req -new -key client.key -out client.csr \ -subj "/c=cn/st=beijing/l=beijing/o=mytestorg/cn=mytestclient" # 3. 使用根ca签发客户端证书 openssl ca -in client.csr -out client.crt -cert certs/ca.crt -keyfile private/ca.key -days 365
此外,客户端证书通常需要转换为pkcs#12格式( .p12 或 .pfx ),以便导入到浏览器或java等应用的密钥库中。
# 将客户端证书和私钥打包为p12格式,需要设置导入密码 openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "myclientcert"
假设我们已经有了证书文件: server.crt , server.key , ca.crt (根证书), client.crt (客户端证书)。
这是配置 https 站点的起点。
server {
listen 443 ssl; # 监听443端口并启用ssl
server_name api.mytest.com;
# 1. 指定服务器证书和私钥(必需)
ssl_certificate /path/to/your/server.crt;
ssl_certificate_key /path/to/your/server.key;
# 2. 优化ssl协议和密码套件(强烈建议)
ssl_protocols tlsv1.2 tlsv1.3; # 禁用不安全的sslv2, sslv3, tlsv1.0, tlsv1.1
ssl_ciphers ecdhe-rsa-aes128-gcm-sha256:ecdhe:ecdh:aes:high:!null:!anull:!md5:!adh:!rc4;
ssl_prefer_server_ciphers on;
# 3. 启用ssl会话缓存,提升性能
ssl_session_cache shared:ssl:10m;
ssl_session_timeout 10m;
# 你的应用配置
location / {
proxy_pass http://backend_server;
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
}
}
# 将http请求重定向到https(标准做法)
server {
listen 80;
server_name api.mytest.com;
return 301 https://$server_name$request_uri;
}关键点解析 :
在单向认证的基础上,增加验证客户端证书的配置。
server {
listen 443 ssl;
server_name api.mytest.com;
ssl_certificate /path/to/your/server.crt;
ssl_certificate_key /path/to/your/server.key;
# 1. 指定用于验证客户端证书的ca证书(必需)
ssl_client_certificate /path/to/your/ca.crt;
# 2. 设置客户端证书验证模式(必需)
ssl_verify_client on; # 或 `optional` | `optional_no_ca`
# `on`: 必须提供且验证通过的有效客户端证书。
# `optional`: 客户端可以提供证书,如果提供则验证。
# `optional_no_ca`: 客户端可以提供证书,即使无法用`ssl_client_certificate`验证也接受。
# 3. 设置验证深度(可选)
ssl_verify_depth 2; # 验证链深度,如果客户端证书由中间ca签发,需要适当调大。
# 优化配置(同单向认证)
ssl_protocols tlsv1.2 tlsv1.3;
ssl_ciphers ecdhe-rsa-aes128-gcm-sha256:ecdhe:ecdh:aes:high:!null:!anull:!md5:!adh:!rc4;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:ssl:10m;
ssl_session_timeout 10m;
location / {
# 4. 将客户端证书信息传递给后端应用(非常有用!)
proxy_set_header x-ssl-client-cert $ssl_client_cert;
proxy_set_header x-ssl-client-verify $ssl_client_verify;
proxy_set_header x-ssl-client-s-dn $ssl_client_s_dn; # 证书主题
proxy_set_header x-ssl-client-i-dn $ssl_client_i_dn; # 颁发者
proxy_pass http://backend_server;
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
}
}
核心配置解读 :
配置完成后,重启nginx ( nginx -s reload ),必须进行验证。
使用 curl 命令测试基础 https 是否工作。
# 测试单向认证(不验证服务器证书,仅用于测试) curl -k https://api.mytest.com/ # 携带根证书,严格验证服务器证书(模拟浏览器行为) curl --cacert /path/to/myca/certs/ca.crt https://api.mytest.com/
第一条命令的 -k 参数会忽略证书验证,能快速确认服务是否监听。第二条命令使用我们自签的ca证书去验证服务器证书,如果成功,说明单向认证的信任链是完整的。
测试双向认证需要客户端提供证书和私钥。
# 使用客户端证书和私钥进行访问
curl --cert ./client.crt --key ./client.key \
--cacert /path/to/myca/certs/ca.crt \
https://api.mytest.com/
# 如果不提供客户端证书,应该被拒绝
curl --cacert /path/to/myca/certs/ca.crt https://api.mytest.com/
# 预期返回 400 bad request: the ssl certificate error
第一条命令成功,第二条命令失败,就证明双向认证配置正确生效了。
对于web应用,你可能需要在浏览器中测试。
把测试环境的配置搬到生产环境,会遇到一系列新问题。
ssl_client_certificate 配置错误 :
客户端证书格式问题 :
性能影响 :
后端应用获取证书信息 :
配置ssl证书,尤其是双向认证,是一个对细节要求极高的工作。从理解原理、生成证书、编写配置到测试排错,每一步都需要仔细。我个人的经验是,遇到问题时,先使用 openssl s_client -connect host:port -cert ... -key ... 命令进行底层连接测试,它能提供最详细的握手和证书信息,比直接调试nginx或应用更高效。把证书信任链理清楚,把nginx的错误日志级别调到 info 或 debug ,大部分问题都能迎刃而解。
到此这篇关于nginx ssl/tls双向认证实战小结的文章就介绍到这了,更多相关nginx ssl/tls双向认证内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论