背景
最近研究了一下MySQL的JDBC传输加密,网上找了很多资料,写得不是很清楚,有疑问的地方也很多,特意研究了一下记录下来。
MySQL服务端默认已开启SSL,可以通过查询have_ssl参数获取,对于JDBC客户端,若版本驱动版本为8.x可以直接通过添加JDBC参数useSSL=true,实现传输加密(驱动版本为5.x还是需要校验证书,配置证书地址,8.x已经是主流的驱动版本,可以不考虑5.x版本),但这种方式默认是不校验证书,使用的证书为MySQL安装时生成的自签名证书,客户端也可以通过开启证书校验,指定证书地址实现更高安全的数据传输,所以有两种方案:
方案一:在保证服务端开启SSL的情况下,添加JDBC参数useSSL=true,不校验证书(verifyServerCertificate默认就为false)实现加密传输。
方案二:在保证服务端开启SSL的情况下,添加JDBC参数useSSL=true,校验证书,并配置证书的文件位置。
1. 配置说明
1.1服务端配置
MySQL默认开启SSL,可通过下面的语句进行查询:
-- 查询服务端是否开启SSL,MySQL默认是启用的,YES启用,DISABLED禁用
SHOW VARIABLES LIKE '%have_ssl%';
若希望显示的启用服务端SSL,可修改mysql配置文件my.cnf,添加配置
#ssl=0为禁用,ssl=1为启用
[mysqld]
ssl=1
1.2客户端配置
方案一:启用SSL,不校验证书
该方案实现较简单,直接配置客户端开启SSL,不做证书校验。
public static void main(String[] args) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
/**
* 方案一:使用服务端的自签名证书,客户端不做校验,默认verifyServerCertificate=false
*/
String url = "jdbc:mysql://169.254.57.111:3306/mysql?useSSL=true";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url,username,password);
Statement statement = connection.createStatement();
//查询当前会话是否使用了加密,若加密会返回加密算法,反之返回空
String sql = "SHOW SESSION STATUS LIKE 'Ssl_cipher'";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("Variable_name="+resultSet.getString("Variable_name"));
System.out.println("Value="+resultSet.getString("Value"));
}
resultSet.close();
statement.close();
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
方案二:启用SSL,校验证书
该方案实现较复杂,首先需要在MySQL服务器上生成证书,然后客户端开启SSL,开启证书校验,并配置证书地址。
证书生成
- 生成证书
#登录mysql服务器,进入/etc/mysql目录(也可以专为证书创建一个目录)
#生成证书密钥
openssl genrsa 2048 > ca-key.pem
#生成证书
openssl req -new -x509 -nodes -days 3600 -key ca-key.pem -out ca-cert.pem -subj "/C=CH/ST=CQ/L=CQ/O=test/OU=Go/CN=ca/emailAddress=test.com"
openssl req -newkey rsa:2048 -days 3600 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=CH/ST=CQ/L=CQ/O=test/OU=Go/CN=Server/emailAddress=test.com"
#生成服务端密钥
openssl rsa -in server-key.pem -out server-key.pem
#生成服务端证书
openssl x509 -req -in server-req.pem -days 3600 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
#验证证书是否正确
openssl verify -CAfile ca-cert.pem server-cert.pem
#查看证书的内容(例如,检查证书有效的日期范围,10年)
openssl x509 -text -in ca-cert.pem
openssl x509 -text -in server-cert.pem
#授权,必须是要做的,否则查看MySQL的日志会看到无法读取证书的错误。
chmod 755 /etc/mysql/*.pem
chown mysql:mysql /etc/mysql/*.pem
#修改MySQL配置文件/etc/mysql/my.cnf,添加证书和密钥位置配置项
[mysqld]
ssl-ca=/etc/mysql/ca-cert.pem
ssl-cert=/etc/mysql/server-cert.pem
ssl-key=/etc/mysql/server-key.pem
客户端配置
将上一步中的MySQL服务器下载ca-cert.pom文件,我这里是下载到D盘下,执行命令将ca-cert.pem文件写入keystore文件,jdbc会直接访问这个文件。
keytool -importcert -alias MySQLCACert -file ca-cert.pem -keystore truststore.jks -storepass 123456
JDBC验证加密访问
public static void main(String[] args) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
/**
* 方案二:自己生成自签名证书,服务端做证书校验
*/
String url = "jdbc:mysql://169.254.57.111:3306/mysql?useSSL=true&requireSSL=true&verifyServerCertificate=true"
+ "&trustCertificateKeyStoreUrl=file:D:/truststore.jks"
+ "&trustCertificateKeyStorePassword=123456";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url,username,password);
Statement statement = connection.createStatement();
//查询当前会话是否使用了加密,若加密会返回加密算法,反之返回空
String sql = "SHOW SESSION STATUS LIKE 'Ssl_cipher'";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("Variable_name="+resultSet.getString("Variable_name"));
System.out.println("Value="+resultSet.getString("Value"));
}
resultSet.close();
statement.close();
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
2. 验证结果
修改前,数据未加密
修改后,数据已加密