旧Android设备连接Nginx出现TLS握手失败(40)问题求助
问题背景
使用官方Docker镜像部署Nginx,需让部分接口兼容运行Android 4.3(JB)或Android 4.4(KitKat)的旧设备。这些设备发起HTTPS请求时抛出以下异常:
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb9d6d1a0: Failure in SSL library, usually a protocol error 2023-11-28 13:43:14.941 8763-8763 System.error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0xa3860990:0x00000000)
通过Wireshark抓包发现:Android客户端发送的SSL Hello包含若干Cipher Suites,但服务器回复「Handshake failure 40」错误。
当前Nginx配置
最初认为是服务器不支持客户端的Cipher Suites,尝试多种配置后,最终使用的配置如下:
server { listen 443 ssl; listen [::]:443 ssl; server_name myserver.com; client_max_body_size 40M; server_tokens off; ssl_certificate /etc/nginx/certs/fullchain.pem; ssl_certificate_key /etc/nginx/certs/key.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA@SECLEVEL=0; access_log /var/log/nginx/host.access.log main; location /api{ proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://mybackend:8080; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } gzip on; }
客户端Hello中的部分Cipher Suites已包含在上述配置中,同时已尝试切换到旧版Nginx镜像(当前使用nginx:1.13)排查默认配置变更的影响。
更新信息
更新1:Nginx日志错误
日志中出现以下握手失败错误:
2023/11/28 18:52:57 [info] 7#7: *1 SSL_do_handshake() failed (SSL: error:1417A0C1:SSL routines:tls_post_process_client_hello:no shared cipher) while SSL handshaking, client: 185.170.137.233, server: 0.0.0.0:443
更新2:服务器暴露的Cipher Suites

疑问
为何部分已在配置中启用的Cipher Suites未被服务器暴露?
解答
1. 证书类型与套件不匹配
你的SSL证书类型会直接过滤不兼容的Cipher Suites:
- 如果是ECDSA证书,所有RSA相关的套件(如
ECDHE-RSA-*、DHE-RSA-*、AES128-SHA等)会被自动忽略,因为ECDSA证书只能搭配ECDSA类套件; - 如果是RSA证书,ECDSA类套件(如
ECDHE-ECDSA-*)也会被过滤。
可在容器内执行以下命令检查证书类型:
openssl x509 -in /etc/nginx/certs/fullchain.pem -text -noout | grep "Public Key Algorithm"
若输出为id-ecPublicKey则是ECDSA证书,需调整配置匹配对应套件。
2. OpenSSL版本限制
nginx:1.13镜像使用的OpenSSL版本可能不支持部分较新的套件(如CHACHA20-POLY1305系列),且旧版OpenSSL对@SECLEVEL=0的处理逻辑可能存在差异。另外,Android 4.3/4.4仅支持有限的旧套件,比如TLS_RSA_WITH_AES_128_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA等,需确保这些套件在配置中且与证书类型匹配。
3. TLS协议版本冲突
Android 4.3/4.4不支持TLS 1.3,配置中保留TLSv1.3可能会干扰握手逻辑,建议移除该协议版本。
4. 修复建议
- 简化
ssl_ciphers配置,只保留Android 4.3/4.4支持且匹配证书类型的套件:# 若为RSA证书,使用此配置 ssl_ciphers "ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA"@SECLEVEL=0; # 若为ECDSA证书,使用此配置 ssl_ciphers "ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA"@SECLEVEL=0; - 调整
ssl_protocols为旧设备支持的版本:ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 重启Nginx后,用以下命令测试握手是否成功:
openssl s_client -connect myserver.com:443 -tls1 -cipher AES128-SHA
内容的提问来源于stack exchange,提问作者Selvaline

