Nginx SSL透传至Spring Boot服务器握手失败问题排查
本地8081端口运行Spring Boot应用,通过自签名证书启用HTTPS,同时启用客户端MTLS认证。需要让Nginx以透传模式(当前配置为反向代理HTTPS到Spring Boot)工作,但出现SSL握手失败错误,报错信息为SSL_do_handshake() failed (SSL: error:0A000412:SSL routines::sslv3 alert bad certificate:SSL alert number 42)。
证书生成步骤
1. 创建CA证书
openssl req \ -new \ -x509 \ -nodes \ -newkey rsa:2048\ -days 365 \ -subj '/CN=bcc' \ -keyout ca.key \ -out ca.crt
2. 生成Spring Boot服务器证书
openssl genrsa \ -out server.key 2048 openssl req \ -new \ -key server.key \ -subj '/CN=bcc-ca-website.org' \ -out server.csr openssl x509 \ -req \ -in server.csr \ -CA ca.crt \ -CAkey ca.key \ -CAcreateserial \ -days 365 \ -extfile v3.ext \ -out server.crt
3. 生成Nginx服务器证书
openssl genrsa \ -out nginx-server.key 2048 openssl req \ -new \ -key nginx-server.key \ -subj '/CN=bcc-ca-website.org' \ -out nginx-server.csr openssl x509 \ -req \ -in nginx-server.csr \ -CA ca.crt \ -CAkey ca.key \ -CAcreateserial \ -days 365 \ -extfile v3.ext \ -out nginx-server.crt
Spring Boot配置(application.yml)
server: port: 8081 ssl: enabled: true key-store: "classpath:server.p12" key-store-password: 123456 key-store-type: PKCS12 client-auth: need enabled-protocols: TLSv1.2 trust-store: "classpath:server.p12" trust-store-type: pkcs12 trust-store-password: 123456
注:已启用客户端MTLS认证,客户端CA及证书通过相同方式创建。
Nginx配置(sites-available/default)
upstream node_basic { server localhost:8081; } server { listen 80; location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; ssl_certificate /etc/ssl/nginx-server.crt; ssl_certificate_key /etc/ssl/nginx-server.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions ssl_session_timeout 24h; location / { proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-NginX-Proxy true; proxy_pass https://node_basic; } }
错误信息
2023/02/02 11:37:51 [error] 22475#22475: *1 SSL_do_handshake() failed (SSL: error:0A000412:SSL routines::sslv3 alert bad certificate:SSL alert number 42) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", upstream: "https://127.0.0.1:8081/", host: "bcc-ca-website.org"
排查与修复方案
1. 核心问题定位
错误alert bad certificate:SSL alert number 42表明:Spring Boot启用了client-auth: need,要求上游请求必须提供合法的客户端证书,但当前Nginx向Spring Boot发起HTTPS请求时,未携带符合要求的客户端证书,导致握手被拒绝。
2. 具体修复步骤
步骤1:为Nginx准备合法的客户端证书
将已创建的客户端证书和密钥整理为Nginx可读取的格式:
# 若需打包为PKCS12格式(可选) openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "client"
输入密码时设置为固定值,比如
123456
步骤2:配置Nginx向上游发送客户端证书
修改Nginx的server块,在proxy_pass配置前添加以下内容:
# 配置向上游发送的客户端证书 proxy_ssl_certificate /etc/ssl/client.crt; proxy_ssl_certificate_key /etc/ssl/client.key; # 配置信任上游服务器的CA(即你的自签名CA) proxy_ssl_trusted_certificate /etc/ssl/ca.crt; proxy_ssl_verify on; proxy_ssl_verify_depth 2;
确保证书文件路径正确,且Nginx进程有权限读取这些文件。
步骤3:修正Spring Boot的Trust Store配置
当前Spring Boot的trust-store指向server.p12,该文件仅包含服务器证书,未包含信任的客户端CA证书。需将客户端CA证书导入到独立的trust store:
keytool -importcert -file client-ca.crt -alias client-ca -keystore truststore.p12 -storetype PKCS12 -storepass 123456
然后修改application.yml中的trust-store配置:
trust-store: "classpath:truststore.p12"
步骤4:验证证书有效性
检查客户端证书是否被Spring Boot的trust store信任:
openssl verify -CAfile client-ca.crt client.crt
3. 若需真正的SSL透传(TCP转发)
如果需求是Nginx不终止SSL,直接将TCP流量转发给Spring Boot,需使用Nginx的stream模块,配置示例:
stream { upstream spring_boot { server localhost:8081; } server { listen 443; proxy_pass spring_boot; } server { listen 80; return 301 https://$host$request_uri; } }
此模式下SSL握手直接在客户端与Spring Boot之间完成,Nginx仅做TCP转发,无需处理证书配置,但需确保Nginx已启用
stream模块。
内容的提问来源于stack exchange,提问作者desertSniper87

