前言
最近在工作中想完成一个java通过jdbc连接SQL Server数据库导出数据字典的功能时发现本地用jdk8和对应的SQL Server驱动(可通过SQLServer官网或者maven库下载-mssql-jdbc-8.2.2.jre8.jar)可以正常连接SQL Server2019,然后当我们导出jar包到项目中打算使用时,发现项目的环境是jdk1.6,这时程序与SQL Server2019无法正常建立连接,因为驱动是要在jre8的环境下才能运行的。这时我们就想换成jdk1.6支持的SQL Server驱动——sqljdbc4.jar,本以为换个驱动即可正常运行,结果发现接下来引发的异常竟让我折腾了一整天。
问题原因
jdk1.6因为安全套接字加密协议的不同,当连接SQL Server2014及以上版本时需要额外的jar包(bcpkix-jdk15on-1.60.jar和bcprov-ext-jdk15on-1.60.jar)和修改java.security(jdk目录下的/jre/lib/security/java.security)使其能成功使用我们引入的包
解决过程
开始时,直接换了jar包后连接本地的SQL Server2019直接报异常:
com.microsoft.sqlserver.jdbc.SQLServerException: 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“java.lang.RuntimeException: Could not generate DH keypair”。
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1352)
at com.microsoft.sqlserver.jdbc.TDSChannel.enableSSL(IOBuffer.java:1533)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:1042)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:817)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:700)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:842)
at java.sql.DriverManager.getConnection(DriverManager.java:582)
at java.sql.DriverManager.getConnection(DriverManager.java:185)
Caused by: javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:190)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1747)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1708)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1691)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1222)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1199)
at com.microsoft.sqlserver.jdbc.TDSChannel.enableSSL(IOBuffer.java:1483)
我在网上各种搜索发现大家都建议升级到jdk7+版本来解决,虽然这样一劳永逸,但是由于生产环境用的是1.6版本并不能轻易升级(很无奈,怪系统太旧了。。。),或者是使用低版本的SQL Server(2012版本或更低版本),但是博主希望的是在1.6版本下且所有SQL Server版本都能使用(还不是因为生产是1.6哇!orz)。由此开始了解决版本问题的漫漫长路。
-
下载上述列出的两个包后放入到jdk目录下jrelibext
-
修改jrelibsecurityjava.security:
找到“List of providers and their preference orders (see above):”下的security.provider信息,将
security.provider.3=com.sun.net.ssl.internal.ssl.Provider
用#号进行注释并用以下代码进行替换(security.provider后有下划线的数字最好也按你们文件原有顺序排列下去)
security.provider.3=net.tobszarny.ssl.java6.provider.BouncyCastleSSLProvider
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
#
# List of providers and their preference orders (see above):
#
security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
#注释了下面这行,并重新添加
#security.provider.3=com.sun.net.ssl.internal.ssl.Provider
#试着奔跑的菜鸟添加的下面这行
security.provider.3=net.tobszarny.ssl.java6.provider.BouncyCastleSSLProvider
security.provider.4=com.sun.crypto.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
security.provider.6=com.sun.security.sasl.Provider
security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.8=sun.security.smartcardio.SunPCSC
security.provider.9=sun.security.mscapi.SunMSCAPI
#试着奔跑的菜鸟添加的下面这行
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
注意事项
在我搜索的方法中,有些人说是直接在原security.provider的末尾加上那两行代码即可,但是博主不行,一直报错(Unsupported curveId: 29)而必须将原有的ssl.Provider注释后替换那两行代码才能成功运行。博主猜测的是如果没有注释掉的话,安全套接字加密就用了原有的类而不是使用我们导入的jar包中的类了,但是暂不能保证是否会对其他安全套接字加密的功能有所影响。
最后再重新连接SQLServer即可,当然如果启动了服务器也要将服务器重启后再重试。
结语及反思
这次解决问题花费的时间很长,主要是一开始卡在找两个jar包的路上,因为在搜索引擎上搜到很多其他csdn的博客给出的jar都是需要积分下载的,很多下载后还货不对板 - _-|||,后来才发现Maven库这个东西,找对应版本的jar包简直不要太方便(在这里也不得不吐槽博主在csdn提供出去下载的东西还不能设置免积分)。
当然也希望以后做的都能用jdk8+,不然解决年代久远的环境问题可太累了。
才疏学浅,如文中有错误,感谢大家指出。