最近碰到个JDBC连接MySQL出现SSL错误的问题。
SSL(Secure Socket Layer:安全套接字层),利用数据加密、身份验证和消息完整性验证机制,为基于TCP等可靠连接的应用层协议提供安全性保证。
SSL协议提供的功能主要有,
1. 数据传输的机密性:利用对称密钥算法对传输的数据进行加密。
2. 身份验证机制:基于证书利用数字签名方法对服务器和客户端进行身份验证,其中客户端的身份验证是可选的。 3. 消息完整性验证:消息传输过程中使用MAC算法来检验消息的完整性。
如果用户的传输不是通过SSL的方式,那么其在网络中数据都是以明文进行传输的,而这给别有用心的人带来了可乘之机。所以,现在很多大型网站都开启了SSL功能。同样地,在我们数据库方面,如果客户端连接服务器获取数据不是使用SSL连接,那么在传输过程中,数据就有可能被窃取。
前几天同事碰到了和这个相关的问题,MySQL 5.6如下两个SSL相关的参数默认值是DISABLED,
MySQL 5.7如下两个SSL相关的参数默认值是YES,
客户端通过如下这个jdbc串连接MySQL 5.6,没问题,
代码语言:javascript复制jdbc:mysql://x.x.x.x:3306/test?characterEncoding=utf8
但是如果连接MySQL 5.7,提示如下警告,
代码语言:javascript复制WARN: Establishing SSL connection without server's identity verification is not recommended.
According to MySQL 5.5.45 , 5.6.26 and 5.7.6 requirements SSL connection must be established by default if explicit option isn't set.
For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'.
You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
如果增加useSSL=false,执行是正常的,
代码语言:javascript复制jdbc:mysql://x.x.x.x:3306/test?characterEncoding=utf8&useSSL=false
如果改为useSSL=true,
代码语言:javascript复制jdbc:mysql://x.x.x.x:3306/test?characterEncoding=utf8&useSSL=true
则会执行错误,
代码语言:javascript复制com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 239 milliseconds ago. The last packet sent successfully to the server was 235 milliseconds ago.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:990)
at com.mysql.jdbc.ExportControlled.transformSocketToSSLSocket(ExportControlled.java:201)
at com.mysql.jdbc.MysqlIO.negotiateSSLConnection(MysqlIO.java:4914)
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1663)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1224)
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2199)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2230)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2025)
at com.mysql.jdbc.ConnectionImpl.(ConnectionImpl.java:778)
at com.mysql.jdbc.JDBC4Connection.(JDBC4Connection.java:47)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:386)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:330)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:247)
at com.test.TestMySQL.main(TestMySQL.java:37)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1937)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1478)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:212)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:957)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:892)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1050)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)
at com.mysql.jdbc.ExportControlled.transformSocketToSSLSocket(ExportControlled.java:186)
... 18 more
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
at com.mysql.jdbc.ExportControlled$X509TrustManagerWrapper.checkServerTrusted(ExportControlled.java:302)
at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:922)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1460)
... 26 more
Caused by: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:153)
at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:79)
at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292)
at com.mysql.jdbc.ExportControlled$X509TrustManagerWrapper.checkServerTrusted(ExportControlled.java:295)
... 28 more
根本原因还是MySQL 5.7提高了安全连接的要求,MySQL 5.6中默认关闭SSL,因此客户端连接,不指定useSSL没事儿,但是连接MySQL 5.7,因为默认SSL打开的,所以在jdbc中未指定useSSL会提示个warning,如果在jdbc中指定useSSL=false,明确不使用SSL连接,自然没问题,但如果使用useSSL=true,是强制使用SSL连接,然而这个不仅要求数据库端ssl配置打开,还需要在客户端做一些SSL的配置,我没操作过,各位自行搜索。
如果服务器端安装了SSL,可以在data_dir中看到自动生成的如下文件,
代码语言:javascript复制ca.pem -- 自签的CA证书,客户端连接也需要提供
server-key.pem -- 服务器端私钥文件
server-cert.pem -- 服务器端证书文件
client-key.pem -- 客户端连接服务器端需要提供的私钥文件
client-cert.pem -- 客户端连接服务器端需要提供的证书文件
public_key.pem -- 私钥/公钥对的共有成员
private_key.pem -- 私钥/公钥对的私有成员
如果用户是采用本地localhost或者socket连接数据库,那么不会使用SSL方式了,可以用s显示当前的连接状态,如下所示,本地连接,当前连接没有在SSL安全连接中,SSL是not in use,
代码语言:javascript复制mysql> s
--------------
mysql Ver 14.14 Distrib 5.7.32, for el7 (x86_64) using EditLine wrapper
Connection id: 7939
Current database:
Current user: root@localhost
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.7.32-log MySQL Community Server (GPL)
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: utf8
Conn. characterset: utf8
UNIX socket: /tmp/mysql.sock
Uptime: 104 days 5 hours 30 min 3 sec
Threads: 7 Questions: 287208 Slow queries: 30 Opens: 4496 Flush tables: 8 Open tables: 1393 Queries per second avg: 0.031
--------------
如果用SSL,因为牵扯到加密的操作,对性能就可能产生一些影响,借鉴这篇博文,提供的测试数据,了解一下。测试中的服务器配置,CPU是32核心,内存是128G,SSD磁盘。为了尽量准确测试QPS,采用全内存查询,因为我们线上热点数据基本都在内存中,按照并发线程数分类:1线程、4线程、8线程、16线程、24线程、32线程、64线程,
P.S. https://www.cnblogs.com/mysql-dba/p/7061300.html
测试数据如下,
从测试数据可以发现,开启SSL后,数据库QPS平均降低了23%左右,相对还是比较影响性能的。从SSL实现方式来看,建立连接时需要进行握手、加密、解密等操作,所以耗时基本都在建立连接阶段,这对于使用短连接的应用程序可能产生更大的性能损耗,不过如果使用连接池或者长连接可能会好许多。
因此,是否采用SSL,取决于系统的安全等级和性能要求,
1. 对于非常敏感核心的数据,或者QPS本来就不高的核心数据,可以采用SSL方式保障数据安全性。
2. 对于采用短连接、要求高性能的应用,或者不产生核心敏感数据的应用,性能和可用性才是首要,建议不要采用SSL方式。
》