sql server版本太老,java客户端连接失败问题定位

news/2025/1/7 6:38:33/文章来源:https://www.cnblogs.com/grey-wolf/p/18653140

背景

最近半路接手了一个系统的优化需求,这个系统有个遗留问题还没解决,随着新需求的上线,系统正式开放使用,这个遗留问题也必须解决。

这个系统大概是下面这样的,支持录入各种数据源的信息(ip、端口、数据库种类、账号密码等):

image-20250104210901249

录入完成后,可以查看这些数据源中的表、表的ddl、表中的列(列名、类型及注释等),也可以查看各个表中的数据。

其中一个数据源,是sql server 2008版本,总是连接失败,更别提获取这个db中的表了。

错误堆栈如下:

image-20250104211502284

定位过程

1、前期处理

在我做新需求的时候,我之前的同事A已经处理过这个问题。这个问题只在线上出现,因为开发测试环境压根没有这么老的数据库版本,在开发测试环境申请一台windows服务器来安装一个这样的老版本数据库,也比较麻烦;所以,同事A在处理的时候,基本是网上查到修改的办法后,直接弄到线上去试试能不能解决。

之前改过两次,第一次是这样:

1、参考附件脚本《配置文件java.security》,修改/usr/local/jdk/bin/java/java.security中的配置项jdk.tls.disabledAlgorithms。
修改内容:删除jdk.tls.disabledAlgorithms配置项的“TLSv1, TLSv1.1”,替换成“DHE”

简单解释下这部分的修改,从前文中的错误堆栈来看,这个问题是和ssl有关系的,我之前猜想的就是,这个sql server和mysql一样,支持使用tls加密传输,保护数据安全;但是,可能sql server 2008版本太老了,不支持tls 1.2/1.3这些,只能使用tls1.1/tls1.0等,但是呢,jdk认为使用tls1.1/1.0不够安全,默认是禁用了的,所以,只要把这个禁用tls1.1/tls1.0的配置给改改,允许jdk使用tls1.1/tls1.0,不就可以连接sql server 2008了吗?

但是,遗憾的是,这个改动之前已经上线试过了,没有生效,还是报错。

另外,在我做新需求的时候,同事A又试了一个改动,把驱动版本升级了下,大家知道java都是使用jdbc去连接数据源的,各个厂商会实现jdbc,之前呢,使用的是sqlserver的4.0版本的驱动,这把,直接弄到了8.4.1,准备搞上去再试试:

image-20250104213141696

2、尝试修改配置禁用加密

我了解到这个情况后,因为需求也比较赶,就没想花大力气来搞这个bug,我们都是内网服务器之间调用,这个加密传输,我感觉不是必要,直接弄成不加密不就行了吗?

我找了下代码,里面有拼接jdbc url的代码:

image-20250104213746728

那直接去掉这个encrypt=true;trustServerCertificate=true,去掉后,本地测试了下连接sql server数据库(不是2008版本),用wireshark抓包看了下,发现客户端发给数据库(sql server常用1433端口)的报文里,还是说加密是开启的:

image-20250104214148592

行吧,我查了下,原来不指定encrypt属性,默认就是true,那我手动指定成false得了。

image-20250104214411744

又抓包看了下,这次有了变化,客户端发过去的报文是说,不使用加密:

image-20250104214503660

服务端返回报文也说,不加密:

image-20250104214556331

但是,我在后续的报文里,发现还是部分加密了的:

image-20250104214722668

这就有得难以理解了。

3、debug驱动代码

所以这时候的思路就是,看看为什么源码里还会加密传输,那只能debug了,看看是不是还有其他选项在控制这块,后面找到如下代码:

image-20250104215227828

在上图中,先是三次握手,再是prelogin(就是前文抓包看到的那部分,如:Encryption: Encryption is available but off (0)),再下来呢,有个if,如果满足这个if,就会开启SSL,此时,就会导致发出去的报文是ssl的,也就是说,只要走了这个if,我们就绕不开ssl,就规避不了这个bug。

那我们看看,怎么绕开这个if吧。

这个if中,左边是个常量,ENCRYPT_NOT_SUP表示不支持加密,image-20250104215651186

右边是个变量,初始化的时候是:

private byte negotiatedEncryptionLevel = TDS.ENCRYPT_INVALID;

后续,什么地方会修改这个变量呢,是在prelogin部分,在处理数据库返回的prelogin响应报文时:

image-20250104215857867

这里,2812行,是直接取响应报文中的值,也就是说,以数据库服务端的为准。

还记得,服务端一般是返回如下值:0。

image-20250104220012549

那这样的话,就会导致那个if条件为true:

image-20250104220149407

这块就有点难办了,这个值是服务端返回的,除非数据库返回ENCRYPT_NOT_SUP,表示不支持加密,否则,这个加密是跑不掉了。但我没太想过要让数据库去改这个配置,毕竟这个库,说是客户端还不少,我不可能去动它,影响太大,可能到时候导致别的客户端要改造。

还有个方向,是通过客户端的传参,去影响服务端的返回值,比如客户端传一个不支持加密,看看服务端的返回值。

但,比较遗憾的是,客户端驱动定死了,只能传下面这两个值,要么ENCRYPT_ON,要么ENCRYPT_OFF:

image-20250104221111123

4、覆盖驱动源码,强行绕过enableSSL方法

当时,我的想法是,把这个if条件改一下,改成:

if (TDS.ENCRYPT_ON == negotiatedEncryptionLevel){tdsChannel.enableSSL(serverInfo.getServerName(), serverInfo.getPortNumber());
}

其实,按我这会的想法,改下面这个地方也不错,想办法传ENCRYPT_NOT_SUP给服务端:

requestedEncryptionLevel = isBooleanPropertyOn(sPropKey, sPropValue) ? TDS.ENCRYPT_ON : TDS.ENCRYPT_OFF;

如何才能修改驱动包的代码呢,改是改不了,但是可以想办法覆盖,方法就是在项目中建同包名同类名的java文件(内容直接从源码文件拷贝),然后修改其中的部分代码即可。

但这次有点意思的是,遇到个以前没见过的问题,报如下错误:

java.lang.SecurityException: signer information does not match signer information

最终在网上查了下,(https://blog.csdn.net/weixin_44070655/article/details/129922513),错误的意思是,我们新建的java文件的一些签名信息不太匹配,这块没细看。最终是需要删除jar包中的如下两个文件:

image-20250104223152701

删除的方式,可以直接用压缩软件打开,删除里面的这两个文件即可,另存为即可。然后把改后的jar包发布到私服(可以修改下坐标),或者是使用maven的如下方式:

image-20250104223331747

最终成功绕过enableSSL了,抓包发现,客户端确实没对包进行加密了,但是,服务端不返回任何报文了。我理解的是,服务端当初在进行prelogin协商时,返回的加密选项是:ENCRYPT_OFF,这个按正常流程,后续就是需要加密的,我们现在强行改了客户端源码,导致服务端陷入了迷思:wtf,客户端怎么回事,怎么没加密,这个客户端有问题?行吧,我不返回了。

最终,我还是放弃了这条路。因为,我上网查了下这个encrypt选项。

https://learn.microsoft.com/zh-cn/sql/connect/jdbc/understanding-ssl-support?view=sql-server-ver16

image-20250104224440595

原来,加密分为两个部分,第一个部分是登录部分,建立连接时,会传输用户名密码,我当时发现:在我上面强行改了客户端驱动,收不到服务端响应时,进行了抓包,发现我可以看到明文密码,当时我也有点惊讶,也反应过来了,难怪要弄ssl加密呢;第二个部分是,后续的数据的加密,如传输的sql语句和执行结果。

当encrypt为true时,两个部分都会加密;而当encrypt为false时,登录部分还是会加密,而数据部分不会加密。

所以,不管怎么说,登录部分总是要加密的,所以我还是不要挑战这条路了,毕竟这是协议规定好了的。

5、增加ssl日志

最终,把修改源码部分,全都回退了。最终的jdbc url选项如下,驱动版本也保留着

;encrypt=false;trustServerCertificate=true;
<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId><version>8.4.1.jre8</version>
</dependency>

说白了,ssl问题依然会有(毕竟encrypt=false,登录部分还是要走ssl),但是,我们可以想办法把ssl过程中的日志打印出来:

System.setProperty("javax.net.debug","ssl:handshake:verbose");

image-20250104225440075

这个呢,会打印ssl过程中的细节(注意,是打印到标准输出的,日志文件里没有,要看看启动java进程时,把标准输出重定向到哪里去了,不能是 > /dev/null这种),类似下面这种,到时候我们上线了再看看日志情况吧:

image-20250104225659003

6、上线后检查ssl日志

这个问题,现在说白了,就是客户端发了ssl握手消息给服务端,正常来说,服务端是要响应的,像下面这样,返回server hello这个报文,其中包含选定的ssl加密套件、服务端证书链等信息:

image-20250105080602411

然后我预期的是,上线后,这个ssl日志能把服务端报错的原因打印出来,结果并没有。

直接就是说,服务端关闭了连接,终止握手:

image-20250105080917662

从后来我找运维抓的包也能看出来,服务端发了tcp关闭的报文:

image-20250105081152690

7、尝试更换客户端驱动版本

此时,有点陷入僵局了。客户端没日志,网络报文也看不出来,那意思是只能看看服务端有没有日志了吧。

然后去找了dba,我现场演示了下,他看了数据库端的日志文件:啥都没有。

他给了我两个方案,一个是这个库太老,后续会复制一个新库出来,这个要等;再一个是,这个库也有其他的项目在用,也是java客户端的,他说帮我问下相关同事。

然后后续我单独加了那个同事,了解了下,他们用的驱动版本是7.4.1,我们目前线上是8.4.1:

<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId><version>7.4.1.jre8</version>
</dependency>

然后,jdbc的url是这样:

image-20250105082000028

最终我们的方向是,换不同版本的驱动看一下,先试上面这个7.4.1.jre8版本。因为之前我也查到过资料,就是xx版本的数据库,需要xx版本的驱动。

https://learn.microsoft.com/zh-cn/sql/connect/jdbc/microsoft-jdbc-driver-for-sql-server-support-matrix?view=sql-server-ver16

image-20250105082313608

从图里能看出来,sql server 2008,需要7.2版本的驱动。我们之前的8.4.1,肯定是高了;其实看上图,7.4也高了,但不知道人家项目为啥能行,就也试试呗。

8、开发测试驱动版本工具类

写了个类来测试:

image-20250105082729476

执行方式就是把jar和class放到同一目录下执行:

[root@news-center-app ~]# ll DbConnectTest.class mssql-jdbc-7.4.1.jre8.jar 
-rw-rw-rw- 1 root root    1631 Dec 30 15:30 DbConnectTest.class
-rw-rw-rw- 1 root root 1209660 Dec 30 15:16 mssql-jdbc-7.4.1.jre8.jar
[root@news-center-app ~]#  java -classpath .:./mssql-jdbc-7.4.1.jre8.jar DbConnectTest "jdbc:sqlserver://1.1.1.1:1433;databaseName=xxx;encrypt=false;trustServerCertificate=true" zhangsan xxx

这样呢,方便我们替换驱动的jar包。

结果呢,运维说必须走流程才能这么玩,理由就是不能在生产上做测试,battle了半天,后面还是提流程了(正好有个小需求又要上,就把这个工具一起弄上去了)。

上线的时候,我们顺便就把之前的驱动版本从8.4.1.jre8改成了7.4.1.jre8,也包括这个小工具。

上线后,以为这次肯定能行,结果,还是报一样的错误,此时正值周五快下班的时候,我无语了:就不能早点解决了这个bug,好好过个周末,不然还牵挂着它。

结果我回家路上,运维在群里说,bug可以了,他上网找了下文章:

https://blog.csdn.net/zhujun300/article/details/141434867

还是修改jdk的java.security文件,这次又把另一个被禁用的给去掉了:3DES_EDE_CBC. (我在开头说,同事A之前就改过一次,但是去的是tls1.1/tls1.0,没去这个3DES_EDE_CBC)。

然后,就好了。

行吧,还是能好好过周末的。

9、为什么去掉3DES_EDE_CBC能好

网上翻了很多资料,没找到讲这块原理的。我自己本地试验了下,在去掉这个3DES_EDE_CBC前,记录了打印的ssl日志;在去掉后,又记录了下。

对比如下,可以看到,去掉后,握手消息中多了很多加密套件(其中都包含了3DES_EDE_CBC这个加密算法):

image-20250105084822878

那这样的话,我们可以认为,线上那个库,应该是不支持客户端发送出去的所有加密套件,才把ssl握手终止了。

而加上3DES_EDE_CBC后,多了一些加密套件,而这些套件,正好服务端就支持,所以就可以了。

当然,具体选择了哪个加密套件,可能得下周上班了再找运维看看日志或者抓个包瞧瞧才知道。

这次呢,我也学会了一个新技能,由于ssl握手消息是封装在其他协议(TDS)里面的,在wireshark中都没法看。

image-20250105085534815

上面蓝色部分就是握手消息,但看不了,要知道握手的具体细节,非得看ssl日志才行,这个让人有点不爽。

我上网找了下,还真找到个网站:

https://williamlieurance.com/tls-handshake-parser/

只要把十六进制流复制进去,就能解析ssl。

image-20250105085747745

image-20250105085817985

对我来说,算是不小的一个收获。

10、怎么查看sql server 2008支持的加密套件

一开始,对这块不理解,以为ssl加密相关能力是sql server 2008这个软件提供的(对windows服务器太不了解了),但后来查了些资料发现,ssl加密相关能力是操作系统提供的;像是linux呢,一般就是安装了openssl,其他软件都是复用openssl的能力。

而sql server 2008,当时查了下版本:

Microsoft SQL Server 2008 (SP1) - 10.0.2531.0 (X64)   Mar 29 2009 10:11:52   Copyright (c) 1988-2008 Microsoft Corporation  Enterprise Edition (64-bit) on Windows NT 5.2 <X64> (Build 3790: Service Pack 2) (VM) 

没细问windows服务器的版本,但我们从上述也能看出来:

windows服务器版本其实就是:Windows NT 5.2 (Build 3790: Service Pack 2)

这个服务器,搜索了下,其实是:win server 2003版本。

image-20250105100603157

难怪了,这个操作系统版本太低了,估计就是很多ssl套件都不支持。

那么,我们如何查看一个windows电脑,支持哪些加密套件呢?

有以下几种方式:

10.1 通过组策略管理器查看

  1. 按下 “Win + R” 键,输入 “gpedit.msc” 并回车,打开组策略编辑器2。
  2. 依次展开 “计算机配置”→“策略”→“管理模板”→“网络”→“SSL 配置设置”2。
  3. 双击右侧的 “SSL 密码套件顺序”,选择 “已启用”,在左下侧可以看到支持的 SSL 加密套件

10.2 通过命令查看

使用 PowerShell:以管理员身份运行 PowerShell,输入Get-TlsCipherSuite命令,可列出当前系统上配置的 TLS/SSL 加密套件以及它们的启用状态等信息。

image-20250105095527943

10.3 IISCrypto

这边下载了一个软件:https://www.nartac.com/Products/IISCrypto/Download

可以大概看到ssl中涉及的几个部分:传输协议、加密算法、hash算法、秘钥交换算法。

image-20250105095223507

通过上述几个部分,组成了各种各样的ssl套件:

image-20250105095317399

官方参考链接

https://learn.microsoft.com/en-us/windows/win32/secauthn/schannel-cipher-suites-in-windows-vista

其他尝试过的定位手段

1、本地安装sql server 2008

我猜测这个问题应该是比较好复现的,只是苦于没有环境,然后就想着在本机装一个,没想到,就这也踩了好久的坑。

安装sql server 2008,依赖.net framework 3.5这个运行环境,我是win10系统。网上的方法分两类,在线安装和离线安装,整个.net framework 3.5包有100多m,在线安装,我这边反正不行,不只是网络的问题,好像在线安装是需要一个service正常运行才可以:windows update。

相信很多人,当初为了禁用windows的升级,可能把这个service都删掉了。

可能也正是因为这样,我甚至离线安装也是失败的。

我也附上几个链接吧,万一大家可以呢:

https://mp.weixin.qq.com/s/y0sZz8wtLtcPQM0sI8DHmQ

https://mp.weixin.qq.com/s/_unsXuBLH1JjtsprKYjSWw

https://mp.weixin.qq.com/s/4FtoTMF3L_hDAXGu_GgOdA

试这些的时候,也可以先看下官方帮助文档:

https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/enable-or-disable-windows-features-using-dism?view=windows-11C:\WINDOWS\system32>dism /online /?
dism /online /Enable-Feature /?
dism /online /Add-Package /?

最终,我用上述这些方法也没成功,后来是按照如下文章来解决的:

https://blog.csdn.net/Roeluo/article/details/144692042

当然,这个.net framework 3.5装上了,并不影响我的sql server 2008安装失败,当然,现在bug都解决了,有空再弄吧。

参考链接

https://blog.csdn.net/wpf416533938/article/details/128573683

https://blog.csdn.net/tanhongwei1994/article/details/84957254

https://learn.microsoft.com/zh-cn/archive/blogs/jdbcteam/the-driver-could-not-establish-a-secure-connection-to-sql-server-by-using-secure-sockets-layer-ssl-encryption

https://stackoverflow.com/questions/32766114/sql-server-jdbc-error-on-java-8-the-driver-could-not-establish-a-secure-connect

https://stackoverflow.com/questions/79113822/java-1-8-sql-server-2008-r2-unable-to-run-query-when-encryption-is-activated

https://www.reddit.com/r/sysadmin/comments/u6grqv/very_legacy_ssl_problem_on_server_2003yep_it/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/864305.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入探索 Kubernetes:从基础概念到实战运维

前言:在当今数字化转型的浪潮中,Kubernetes 已然成为云原生应用部署与管理的核心力量。无论是初创企业寻求敏捷开发,还是大型企业应对复杂业务架构,掌握 Kubernetes 都能为你的技术之旅赋能。今天,就让我们一同深入 Kubernetes 的奇妙世界,揭开它神秘的面纱,从基础概念到…

Python 将html格式书签转为excel

1.导出html格式书签2.对数据做处理 提取 <a></a> 标签 可以用vscode正则替换 <a></a> 标签的ICON属性ICON=".*"3.安装python pip替换成清华镜像 4.描述问题生成代码 import pandas as pd from bs4 import BeautifulSoup from datetime i…

UML之发现用例

用例是最简单的UML元素,用例图是最简单的UML图,但它也可能是UML中最有用的元素之一。尽管我们用包将工作分解为工作包、团队任务或单项任务,也就是说包是组织UML中的各种图及元素的工具。但是用例图可以帮助我们确定任务,以及应当如何将它们分组并确定工作范围。 每个用例都…

遗留了很久的功能终于搞定/QTreeWidget自定义节点/添加删除修改分组

一、前言说明 这个功能看起来简单,实际上也确实简单,以前没搞的时候还以为很难,难点就是如何存储这个任意层级的树状列表信息,近期大环境经济很差,刚好有空把这个功能搞定,其实二维表格的方式存储这种任意层级树结构就可以,就是子节点需要指定父节点,父节点为空表示顶层…

2025年正在重塑行业的10款AI代理工具

序言:本文的作者列出来的这10款AI代理工具是您认可的吗? 作为一名深入AI开发领域超过十年的开发者,我见过无数工具声称要颠覆我们构建AI代理的方式。有些工具确实实现了夸下的海口——但更多的则没有。 经过几个月的亲身测试以及与同行开发者的讨论,我整理出了一份2025年真…

[cause]: TypeError: e_.createContext is not a function (Next.js 15)

开发 Next.js 项目遇到报错: [cause]: TypeError: e_.createContext is not a function 出现这个报错的原因是在 Next.js 项目中,在 Server Component 中使用了MUI组件,但是MUI组件没有做 SSR 适配就会导致这个报错。 解决办法 解决办法就是在文件顶部添加 use client 声明…

golang自带的死锁检测并非银弹

网上总是能看到有人说go自带了死锁检测,只要有死锁发生runtime就能检测到并及时报错退出,因此go不会被死锁问题困扰。 这说明了口口相传知识的有效性是日常值得怀疑的,同时也再一次证明了没有银弹这句话的含金量。 这个说法的杀伤力在于它虽然不对,但也不是全错,真真假假很…

2025多校冲刺省选模拟赛2

2025多校冲刺省选模拟赛2\(T1\) A. aw \(10pts/20pts\)部分分\(10 \sim 20pts\) :枚举每一种定向方案,略带卡常。点击查看代码 const int p=998244353; struct node {int nxt,to; }e[200010]; int head[100010],dis[1010][1010],a[100010],b[100010],g[2][100010],cnt=0; b…

jamovi 2.6 (Linux, macOS, Windows) - 统计软件

jamovi 2.6 (Linux, macOS, Windows) - 统计软件jamovi 2.6 (Linux, macOS, Windows) - 统计软件 open statistical software 请访问原文直链:https://sysin.org/blog/jamovi/ 查看最新版。原创作品,转载请保留出处。 作者主页:sysin.orgjamovi适用于桌面和云的开放式统计软…

读数据保护:工作负载的可恢复性26商用数据备份方案

商用数据备份方案1. 备份简史 1.1. 20世纪80年代中期大家都还没有意识到,运行着商用UNIX操作系统的大型工作环境里,应该配备一款商用的备份软件或某种自动的磁带系统 1.2. 1993年备份工作全都是通过shell脚本与cron job形式的计划任务来实现的1.2.1. 脚本总是假定服务器中需要…

OpenCV和OpenVX有什么联系和区别

OpenCV和OpenVX有什么联系和区别 联系和区别是:OpenCV是一个基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习软件库。OpenVX 实现了跨平台加速处理,OpenVX在嵌入式和实时性系统中,可以更好地发挥它的优势,在某些场合配合OpenCV的强大功能,可以实现更好的效果。…

SPIR-V生态系统概述

SPIR-V生态系统SPIR-V生态系统,如图1-42所示。图1-42 SPIR-V生态系统 1.4.2 OpenVX路线图OpenVX路线图,如图1-43所示。图1-43 OpenVX路线图 OpenVX跨供应商视觉与推理 基于图形的高级抽象实现可移植、高效的视觉处理。 1)处理器供应商创建、优化和发布优化OpenVX的驱动程序。…