Java 连 SQL Server 报 TLS10 is not accepted?先查 JDK 默认禁用了啥

3 阅读4分钟

Java 连 SQL Server 报 TLS10 is not accepted?先查 JDK 默认禁用了啥

现象:驱动 SSL 握手失败,提示服务器选了 TLS1.0,客户端只接受 TLS1.2/1.3。
结论:高版本 JDK 默认禁用 TLSv1/TLSv1.1;在无法升级库端 TLS 时,可临时放开 jdk.tls.disabledAlgorithms,长期仍应升到 TLS1.2+。

1. 现象

用 Microsoft JDBC 连接 SQL Server 时,控制台出现类似:

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException:
驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。
错误:“The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]”

Caused by: javax.net.ssl.SSLHandshakeException:
The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]

人话版:客户端只愿意用 TLS1.2/1.3,服务端却只给出 TLS1.0,握手谈不拢。

2. 环境

典型值(按你的现场改)
JDK8u291+ / 11 / 17(越新越容易踩)
驱动mssql-jdbc
数据库SQL Server(仅启用 TLS1.0,或未开 TLS1.2)
部署本地 / 测试 / 生产(勿写内网细节)

分水岭:Oracle JDK 8u291 起默认禁用 TLS1.0/1.1;后续 11、17 同样默认禁用。

3. 排查路径

按这个顺序查,避免一上来乱改配置:

  1. 确认报错关键字 → 有 TLS10 is not accepted by client preferences [TLS13, TLS12],基本可断定是协议版本协商失败,不是账号密码错。
  2. 确认本机/容器实际 JDKjava -version,看是不是 8u291+ 或更高大版本(容器里经常「以为是 8,实际是 17」)。
  3. 确认库端 TLS 能力 → 运维侧看 SQL Server 是否只开了 TLS1.0;能开 TLS1.2 就优先开,后面步骤可省略。
  4. 确认改的是正在跑的那份 JDK → 机器上多 JDK 时,改错目录等于没改;以进程的 JAVA_HOME / 启动脚本为准。
  5. 仍失败再查外围 → 防火墙、反向代理、负载均衡是否做了 TLS 终结或拦截。

4. 根因

一句话:库端还在 TLS1.0,客户端 JDK 默认已经把 TLS1.0/1.1 拉黑了。

  • SQL Server 端:仅启用 TLS1.0(或业务库长期未升级加密协议)。
  • Java 端:从 JDK 8u291 起,安全策略默认禁用 TLSv1TLSv1.1
  • 握手时服务端选出 TLS1.0,客户端拒绝 → SSLHandshakeException

这不是 JDBC「突然坏了」,而是 JDK 安全基线抬高 后与旧库不兼容。

5. 解法

优先方案(推荐):升级 SQL Server 侧 TLS 到 1.2+

从安全与合规看,这是正解。客户端改安全文件只是权宜之计。

临时方案:放开 JDK 对 TLS1.0/1.1 的禁用

步骤 1:定位 java.security(改前先备份)

JDK常见路径
JDK 8$JAVA_HOME/jre/lib/security/java.security
JDK 11+$JAVA_HOME/conf/security/java.security

示例(JDK 8):

/path/to/jdk1.8/jre/lib/security/java.security

步骤 2:改 jdk.tls.disabledAlgorithms

找到类似配置:

# before(示例,以你文件实际内容为准)
jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, ...

本问题的最小必要改动是去掉对 TLS1.0/1.1 的禁用,例如:

# after:去掉 TLSv1、TLSv1.1(其余项按你们安全基线保留)
jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, ...

说明:

  • 真正卡住握手的通常是 TLSv1 / TLSv1.1
  • 是否同时去掉 3DES_EDE_CBC 取决于服务端 cipher;不要无脑整行删光,只删协商失败相关的项,并保留团队安全要求的其他算法禁用。
  • 不同 JDK 小版本该行内容不一致,以本地文件为准做 Diff,不要整行复制网上示例覆盖。

步骤 3:重启使用该 JDK 的 Java 进程

只改文件不重启不会生效。改完再连库验证。

不推荐但常见的「假解决」

  • 连接串里关加密(如盲目 encrypt=false):可能绕过现象,但把明文风险带进环境,生产慎用,且新版本驱动默认行为也在变。
  • 只改了本机 JDK、容器/CI 仍是另一份 JDK:表现为「我电脑好了,流水线还挂」。

6. 防再发

  • 新环境连旧 SQL Server:先确认库端 TLS ≥ 1.2,再谈应用配置
  • 多 JDK / 容器镜像:改配置前确认进程真实 JAVA_HOME
  • java.security 变更走变更单 + 备份 + 回滚说明
  • 临时放开 TLS1.0 要记技术债,限期推动库端升级
  • 把本机验证通过的 JDK 路径写进 README / 部署文档,避免同事改错目录

#祖传代码排障 #Java #SQLServer #TLS #SSL #JDK


关于我

「Java祖传代码守护者」——记录真实 Java 开发踩坑,并把解法沉淀成可复用的知识。

若本文帮到你,欢迎点赞、收藏;有同类坑欢迎评论区补充场景。