目录
一、MySQL Server和Client多种不同SSL配置情况的连接测试
环境信息:Centos7 + MySQL 5.7.21
在该环境上进行SSL连接测试。
一、MySQL Server和Client多种不同SSL配置情况的连接测试
MySQL Server 配置:
vi /etc/my.cnf
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
# ssl_mode = [ DISABLED | PREFERRED(default if not set) | REQUIRED | VERIFY_CA | VERIFY_IDENTITY ]
# ssl_mode 为客户端可选参数,如果客户端连接时指定了则生效的是客户端连接时指定的参数,如果客户端连接未指定则使用服务端的[client]中的配置参数,如果未配置默认为PREFRED。
# DISABLED: 非SSL连接。
# PREFERRED: SSL/no-ssl。如果不配置、默认就是该行为。先尝试SSL连接、如果失败则尝试非SSL连接。
# REQUIRED: SSL连接。仅尝试SSL连接,SSL连接失败则失败、不会尝试非SSL连接。
# VERIFY_CA: SSL连接,双向认证。校验ca证书、但不校验证书中的server host name。
# VERIFY_IDENTITY: SSL连接,双向认证。校验ca证书、同时校验证书中的server host name。
#ssl_mode = PREFERRED
ssl_mode = VERIFY_CA
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
validate_password=off
# for ssl enable
# 服务器端配置强制SSL连接、禁止非SSL连接
require_secure_transport = ON
# 下面3个参数如果不配置或者值配置成ca.pem, server-cert.pem, server-key.pem,不加路径,则MySQL默认行为是读取数据目录下的三个文件
ssl-ca=/var/lib/mysql/ssl-cert/ca.pem
ssl-cert=/var/lib/mysql/ssl-cert/server-cert.pem
ssl-key=/var/lib/mysql/ssl-cert/server-key.pem
#for mysqlbinlog enable
server-id=1
log-bin=/var/lib/mysql/mysql-bin
binlog_format=row
expire_logs_days=15
max_binlog_size=500m
#utf8mb4
character-set-client-handshake=false
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
# for blob input, default value is 4194304 (4M) if not set
#max_allowed_packet = 100M
# for other performance settings
max_connections = 2000
max_connect_errors = 500
back_log = 1000
thread_cache_size = 64
table_open_cache_instances = 500
###show global status like '%open%table%'; Open_tables / Opened_tables 85%~95%
table_open_cache = 10000
table_definition_cache = 2000
innodb_thread_concurrency = 0
max_tmp_tables = 100
## common sql commands
## -- show engine innodb status \G;
## -- show global variables like '%thread%';
## -- show global status like '%thread%';
## -- show global status like '%open%table%';
## -- show variables like '%table%';
# Disablinr symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
编写测试脚本:
vi cmd.sh
#!/bin/sh
# ssl-test-qftools
SQL_SSL="
# check if server start with --ssl:\n
# -- show variables like '%have_ssl%';\n
\n
# check if server force accpet ssl session request ( deny no-ssl requests ):\n
# -- show variables like 'require_secure_transport';\n
\n
# client connected and check current session ssl status:\n
# -- show session status like 'Ssl_cipher';\n
\n
# check ssl configs:\n
# -- show variables like '%ssl%';\n
"
# java keystore.jks generate cmd: keytool -importcert -trustcacerts -file ca.pem -keystore keystore216.jks -storepass 1q2w3e
HOST=192.168.1.216
PORT=3306
USER=root
DIR=./ssl-cert
# for MySQL old Versin
#$SSLOPT=--ssl
#$SSLOPT="--ssl --ssl-verify-server-cert"
# for MySQL new Version
SSLOPT_DISABLED="--ssl-mode=DISABLED"
SSLOPT_PREFERRED="--ssl-mode=PREFERRED"
SSLOPT_REQUIRED="--ssl-mode=REQUIRED"
# ssl && also perform verification against the server CA certificate but no against the server host name in its certificate.
SSLOPT_CA="--ssl-mode=VERIFY_CA"
# ssl && also perform verification against the server CA certificate and (with VERIFY_IDENTITY) against the server host name in its certificate.
SSLOPT_IDENTITY="--ssl-mode=VERIFY_IDENTITY"
CA=$DIR/ca.pem
CERT=$DIR/client-cert.pem
KEY=$DIR/client-key.pem
# 客户端连接时不指定相关参数、由服务器配置决定最终行为
CMD0="mysql -h$HOST -P$PORT -u$USER -p "
# 客户端连接指定配置
# no-ssl
CMD1="mysql -h$HOST -P$PORT -u$USER -p $SSLOPT_DISABLED"
# ssl/no-ssl 先尝试SSL连接、如果失败则尝试非SSL连接
CMD2="mysql -h$HOST -P$PORT -u$USER -p $SSLOPT_PREFERRED"
# ssl单向,SSL连接失败则失败、不会尝试非SSL连接
CMD3="mysql -h$HOST -P$PORT -u$USER -p $SSLOPT_REQUIRED"
# ssl单向、虽然多余传ca证书、客户端证书和秘钥(实际上无需传),实际效果还是单向。MySQL Server my.cnf [client] 中如果配置为 ssl_mode = VERIFY_CA ,连接时客户端如果指定ssl_mode参数则会覆盖,以客户端配置优先
CMD4="mysql -h$HOST -P$PORT -u$USER -p $SSLOPT_REQUIRED --ssl-ca=$CA --ssl-cert=$CERT --ssl-key=$KEY"
# ssl双向认证,不验证hostname
CMD5="mysql -h$HOST -P$PORT -u$USER -p $SSLOPT_CA --ssl-ca=$CA --ssl-cert=$CERT --ssl-key=$KEY"
# ssl双向认证 && 验证hostname
CMD6="mysql -h$HOST -P$PORT -u$USER -p $SSLOPT_IDENTITY --ssl-ca=$CA --ssl-cert=$CERT --ssl-key=$KEY"
if [ ! $# -eq 1 ] || [ "$1" == "-h" ];then
echo ""
echo "Please user: $0 {type}"
echo " type: 0 | 1 | 2 | 3 | 4 | 5 | 6"
echo " type 0 for run: $CMD0"
echo " type 1 for run: $CMD1"
echo " type 2 for run: $CMD2"
echo " type 3 for run: $CMD3"
echo " type 4 for run: $CMD4"
echo " type 5 for run: $CMD5"
echo " type 6 for run: $CMD6"
echo -e "\n---------------------\n"
echo "About sql for show ssl infos after mysql connected: "
echo -e $SQL_SSL
exit
fi
inputType=$1
if [ -z inputType ];then
inputType=1
fi
echo "inputType = $inputType"
if [ $inputType -eq 0 ];then
echo $CMD0
$CMD0
elif [ $inputType -eq 1 ];then
echo $CMD1
$CMD1
elif [ $inputType -eq 2 ];then
echo $CMD2
$CMD2
elif [ $inputType -eq 3 ];then
echo $CMD3
$CMD3
elif [ $inputType -eq 4 ];then
echo $CMD4
$CMD4
elif [ $inputType -eq 5 ];then
echo $CMD5
$CMD5
elif [ $inputType -eq 6 ];then
echo $CMD6
$CMD6
else
echo "ERROR: inputType unsupport!"
exit
fi
从MySQL Server服务器拷贝证书和Key到客户端机器上,然后客户端机器上执行相关测试:
[root@localhost ssl_test]# ls -l
total 4
-rwxr--r-- 1 root root 3165 Apr 25 01:40 cmd.sh
drwxr-xr-x 2 root root 62 Apr 24 21:45 ssl-cert
[root@localhost ssl_test]# ls -l ssl-cert/
total 12
-rw-r--r-- 1 root root 1107 Apr 24 21:45 ca.pem
-rw-r--r-- 1 root root 1107 Apr 24 21:45 client-cert.pem
-rw------- 1 root root 1679 Apr 24 21:45 client-key.pem
SSL连接测试失败的情况:
./cmd.sh 0
./cmd.sh 1
./cmd.sh 6
SSL连接失败的执行详细信息:
[root@localhost ssl_test]# ./cmd.sh 0
inputType = 0
mysql -h192.168.1.216 -P3306 -uroot -p
Enter password:
ERROR 2026 (HY000): SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY
[root@localhost ssl_test]#
[root@localhost ssl_test]# ./cmd.sh 1
inputType = 1
mysql -h192.168.1.216 -P3306 -uroot -p --ssl-mode=DISABLED
Enter password:
ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON.
[root@localhost ssl_test]#
[root@localhost ssl_test]# ./cmd.sh 6
inputType = 6
mysql -h192.168.1.216 -P3306 -uroot -p --ssl-mode=VERIFY_IDENTITY --ssl-ca=./ssl-cert/ca.pem --ssl-cert=./ssl-cert/client-cert.pem --ssl-key=./ssl-cert/client-key.pem
Enter password:
ERROR 2026 (HY000): SSL connection error: SSL certificate validation failure
[root@localhost ssl_test]#
SSL连接测试成功的情况:
./cmd.sh 2
./cmd.sh 3
./cmd.sh 4
./cmd.sh 5
注意:
1)如果 Java demo ssl 方式连接 MySQL 时报错:
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
SSL握手异常,可能是因为Java高版本默认配置禁用了SSL协议或加密套件。
找到 java 安全配置文件 java.security,修改配置。
查看 Java 版本
java -version
查看 java 路径
whereis java
ls -l /usr/bin/java
ls -l /etc/alternatives/java
find /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.412.b08-1.el7_9.x86_64 -name java.security
文件位置在:jre/lib/security/java.security
修改安全配置文件:
vim /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.412.b08-1.el7_9.x86_64/jre/lib/security/java.security
找到配置参数并修改。
修改前:
# Example:
# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
include jdk.disabled.namedCurves
修改后(取消 TLSv1, TLSv1.1, 禁用配置):
# Example:
# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
#jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
# DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
# include jdk.disabled.namedCurves
jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, \
DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
include jdk.disabled.namedCurves
2)如果 Java demo ssl 方式连接 MySQL 时报错:
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
这是在JDK8及以上高版本使用中,因为源应用程序不信任目标应用程序的证书,在源应用程序的JVM信任库中找不到该证书或证书链的原因导致的。
需要手动生成证书文件,然后放到jdk对应目录下,重启就行。
生成的证书文件存放位置:jdk\jre\lib\security
或者在Java应用程序中设置 keyStore 和 trustStore:
System.setProperty("javax.net.ssl.keyStore", keyStoreFilePath);
System.setProperty("javax.net.ssl.keyStorePassword", "123456");
System.setProperty("javax.net.ssl.trustStore", trustStoreFilePath);
System.setProperty("javax.net.ssl.trustStorePassword", "123456");
# 将服务器证书导入到 truststore
keytool -import -alias CACert -file ca.pem -keystore truststore
# 将客户端证书和私钥导入到 keystore
keytool -import -alias ClientCert -file client-cert.pem -keystore keystore
MySQL SSL证书的生成方法:
方法1)可以参考:《GBase8a SSL 配置_gbase配置外部访问-CSDN博客》
方法2)可以参考本文后面《执行 mysql_ssl_rsa_setup 安装命令》,默认在 mysql bin 目录下带有 mysql_ssl_rsa_setup 执行文件。执行它开始安装 ssl,执行后会生成 ssl 相关秘钥和证书文件。
3)如果报错:Caused by: java.security.cert.CertificateNotYetValidException: NotBefore: Wed Aug 21 20:51:33 CST 2024
说明MySQL客户端和MySQL服务端的时间不同步。
二、MySQL SSL配置开启的配置流程
1. MySQL Server Create SSL/RSA Files 的官方介绍
《4.4.5 mysql_ssl_rsa_setup — Create SSL/RSA Files》
MySQL 8.0:https://dev.mysql.com/doc/refman/8.0/en/mysql-ssl-rsa-setup.html
MySQL 5.7:https://dev.mysql.com/doc/refman/5.7/en/mysql-ssl-rsa-setup.html
2. MySQL Server 配置SSL功能开启的官方介绍
《8.3.1 Configuring MySQL to Use Encrypted Connections》
MySQL 8.0:https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html
MySQL 5.7:https://dev.mysql.com/doc/refman/5.7/en/using-encrypted-connections.html
3. 我以MySQL配置开启操作步骤示例
1) 先查看MySQL Server的SSL功能是否已开启
mysql> show variables like "%ssl%";
查看 have_openssl、have_ssl 的值是否为 Yes
mysql> show variables like "%ssl%";
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| have_openssl | DISABLED |
| have_ssl | DISABLED |
| ssl_ca | |
| ssl_capath | |
| ssl_cert | |
| ssl_cipher | |
| ssl_crl | |
| ssl_crlpath | |
| ssl_key | |
+---------------+----------+
2) 停止 MySQL 服务
service mysqld stop
service mysqld status
或
systemctl stop mysqld
systemctl status mysqld
3) 执行 mysql_ssl_rsa_setup 安装命令
默认在 mysql bin 目录下带有 mysql_ssl_rsa_setup 执行文件。执行它开始安装 ssl,执行后会生成 ssl 相关秘钥和证书文件(ca证书和私钥、client证书和私钥、server证书和私钥、private/public公私钥对)、总共8个文件。
注意:如果不是使用 mysql_ssl_rsa_setup 生成、一次执行会生成8个文件。如果是自己使用 openssl 命令生成私钥和证书的话,按需生成就行,如果单向认证,只需要生成ca证书、服务端私钥和服务端证书即可,无需生成客户端证书和私钥。
mkdir -p /var/lib/mysql/ssl-cert
chown -R mysql:mysql /var/lib/mysql/ssl-cert
mysql_ssl_rsa_setup --datadir=/var/lib/mysql/ssl-cert --user=mysql --uid=mysql
执行命令结果如下:
[root@localhost mysql]# mysql_ssl_rsa_setup --datadir=/var/lib/mysql/ssl-cert --user=mysql --uid=mysql
Generating a 2048 bit RSA private key
............+++
............................................+++
writing new private key to 'ca-key.pem'
-----
Generating a 2048 bit RSA private key
....................................+++
..................................................+++
writing new private key to 'server-key.pem'
-----
Generating a 2048 bit RSA private key
.................+++
................+++
writing new private key to 'client-key.pem'
-----
[root@localhost mysql]#
[root@localhost mysql]#
[root@localhost mysql]# ls -l ssl-cert/
total 32
-rw------- 1 mysql mysql 1675 Aug 22 17:30 ca-key.pem
-rw-r--r-- 1 mysql mysql 1107 Aug 22 17:30 ca.pem
-rw-r--r-- 1 mysql mysql 1107 Aug 22 17:30 client-cert.pem
-rw------- 1 mysql mysql 1675 Aug 22 17:30 client-key.pem
-rw------- 1 mysql mysql 1679 Aug 22 17:30 private_key.pem
-rw-r--r-- 1 mysql mysql 451 Aug 22 17:30 public_key.pem
-rw-r--r-- 1 mysql mysql 1107 Aug 22 17:30 server-cert.pem
-rw------- 1 mysql mysql 1679 Aug 22 17:30 server-key.pem
[root@localhost mysql]#
说明:
--datadir 参数指定生成文件的保存路径,如果不单独创建目录、也可以直接指定到 mysql data 目录,注意,保存生成文件的目录,一定要有mysql用户访问的权限,所以,一般都是放在 mysql data 目录下。
--user 和 --uid 参数如果不加,默认为mysql
如果不单独创建目录,直接生成文件放在 msyql data 目录下则可以执行:
mysql_ssl_rsa_setup --datadir=/var/lib/mysql --user=mysql --uid=mysql
也可以执行时不加任何参数,默认以mysql用户执行、生成的文件在的默认路径为 mysql data 目录
mysql_ssl_rsa_setup
4)修改MySQL配置文件my.cnf、设置ssl相关参数
vim /etc/my.cnf
[mysqld]
ssl-ca=/var/lib/mysql/ssl-cert/ca.pem
ssl-cert=/var/lib/mysql/ssl-cert/server-cert.pem
ssl-key=/var/lib/mysql/ssl-cert/server-key.pem
6)启动MySQL
service mysqld start
service mysqld status
或
systemctl start mysqld
systemctl status mysqld
7)登录MySQL查看ssl相关配置和当前连接的ssl状态的相关命令
mysql> show variables like "%ssl%"; -- server ssl cfg: have_openssl & have_ssl
mysql> \s; -- current session ssl: SSL
mysql> status; -- current session ssl: SSL
mysql> show status like '%ssl%'; -- server ssl cfg (ssl variables): Ssl_cipher
8)服务端强制配置指定某个用户必须使用SSL连接
-- 方式1:create user 创建时强制指定使用 SSL 连接
-- drop user frank;
-- for remote login
mysql> create user 'frank'@'%' identified by '1q2w3e' REQUIRE SSL;
mysql> grant all on *.* to 'frank'@'%';
mysql> flush privileges;
mysql> show grants for 'frank'@'%';
mysql> select host, user, ssl_type, ssl_cipher from mysql.user;
-- 方式3:alter user 指定强制使用 SSL 连接
mysql> alter user 'frank'@'%' REQUIRE SSL;
mysql> flush privileges;
-- 如果要取消SSL,则执行
mysql> alter user 'frank'@'%' require none;
mysql> flush privileges;
9)查询MySQL所有用户的ssl配置
mysql> select user, host, ssl_type, ssl_cipher from mysql.user;
10)客户端以SSL方式连接测试
i)命令行访问
mysql -h10.80.93.40 -P3306 -ufrank -p --ssl-mode=REQUIRED --ssl-ca=./ssl-cert/ca.pem --ssl-cert=./ssl-cert/client-cert.pem --ssl-key=./ssl-cert/client-key.pem
mysql> status;
......
SSL: Cipher in use is DHE-RSA-AES256-SHA
......
详细介绍参考官方手册:《8.3.1 Configuring MySQL to Use Encrypted Connections》
https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html#using-encrypted-connections-client-side-configuration
https://dev.mysql.com/doc/refman/5.7/en/using-encrypted-connections.html#using-encrypted-connections-client-side-configuration
多种MySQL Server服务端的不同配置、以及MySQL CLient客户端的不同传参的连接方式的测试情况,参考本文签名第一部分的介绍(一、MySQL Server不同配置 & MySQL CLient不同连接传参的多种情况测试)
ii)Java 应用访问
官方关于Java JDBC SSL连接的介绍参考:MySQL :: MySQL Connector/J Developer Guide :: 6.9 Connecting Securely Using SSL
服务器身份验证:
MySQL :: MySQL Connector/J Developer Guide :: 6.9.1 Setting up Server Authentication
客户端身份验证:
MySQL :: MySQL Connector/J Developer Guide :: 6.9.2 Setting up Client Authentication
SSL加密连接有单向认证和双向认证的区分,单向认证客户端不需要生成客户端证书、仅服务端配置指定ca证书和服务端证书和服务端私钥即可。双向认证需要生成客户端证书。
具体 Java 客户端连接时,jdbcRUL 需要用到哪些文件(keystore、trustore),由 jdbcURL 的传参决定。
下面加单测试几种情况:
方式a)加密连接、但客户端不使用证书
&useSSL=true&requireSSL=true&verifyServerCertificate=false
方式b)加密连接、且客户端使用证书、设置服务器身份验证(verifyServerCertificate=true 模拟命令行的 VERIFY_CA,用到 trustore)
把服务器端的 ca.pem 证书拷贝到客户端,再执行命令,添加到 truststore 密钥库(执行完会生成一个 truststore 文件):
设置服务器身份验证:导入服务器证书
keytool -importkeystore -alias MySQLCACert -file ca.pem -keystore truststore -storepass 123456
再在 jdbcURL 中指使用 truststore 文件路径和密码:
&useSSL=true&requireSSL=true&verifyServerCertificate=true&trustCertificateKeyStorePassword=123456&trustCertificateKeyStoreUrl=file:/home/frank/ssl-cert/truststore
注意:
1)trustCertificateKeyStoreUrl 也支持 http 协议,例如:trustCertificateKeyStoreUrl =http://192.168.1.250:8080/truststore
2)trustCertificateKeyStorePassword 和 trustCertificateKeyStoreUrl 也可以不在 jdbcURL 中指定、而直接设置 java 启动参数:
-Djavax.net.ssl.trustStore=/home/frank/ssl-cert/truststore -Djavax.net.ssl.trustStorePassword=123456
也可以在代码中设置:
private void setSysProperties(String target) {
String keyStorePath = System.getProperty("user.dir") + "/ssl-cert/keystore";
String trustStorePath = System.getProperty("user.dir") + "/ssl-cert/truststore";
if ("keyStore".equalsIgnoreCase(target) || "all".equalsIgnoreCase(target)) {
System.setProperty("javax.net.ssl.keyStore", keyStorePath);
System.setProperty("javax.net.ssl.keyStorePassword", "123456");
}
if ("trustSotre".equalsIgnoreCase(target) || "all".equalsIgnoreCase(target)) {
System.setProperty("javax.net.ssl.trustStore", trustStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", "123456");
}
}
private void removeSysProperties() {
System.getProperties().remove("javax.net.ssl.keyStore");
System.getProperties().remove("javax.net.ssl.keyStorePassword");
System.getProperties().remove("javax.net.ssl.trustStore");
System.getProperties().remove("javax.net.ssl.trustStorePassword");
}
public void connWithSSL2() {
String url = "jdbc:mysql://192.168.1.40:3306/testdb?useSSL=true&requireSSL=true&verifyServerCertificate=true";
String user = "test";
String pwd = "123456";
Connection conn = null;
Statement st = null;
ResultSet rs = null;
setSysProperties("all");
try {
Class.forName("com.mysql.jdbc.Driver");
DriverManager.setLoginTimeout(1);
conn = DriverManager.getConnection(url, user, pwd);
st = conn.createStatement();
rs = st.executeQuery("show status like '%SSL%'");
while (rs.next()) {
if (rs.getString(1).startsWith("Ssl_cipher") ||
rs.getString(1).startsWith("Ssl_ver")) {
System.out.println(rs.getString(1) + "-----"
+ rs.getString(2).substring(0,min(50,rs.getString(2).length())));
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
removeSysProperties();
closeResource(conn, st, rs);
}
}
方式c)加密连接、且客户端使用证书、设置服务器身份验证+客户端身份验证(同时用到 keystore、trustore)
设置服务器身份验证:导入服务器证书、生成 truststore JKS文件:
执行该步骤后会提示输入认证密码,比如输入123456(jdbc连接会用到), 提示是否信任证书:Trust this certificate? [no]: 输入 yes 即可:
keytool -importkeystore -alias MySQLCACert -file ca.pem -keystore truststore -storepass 123456
设置客户端身份验证:将客户端密钥(client-key.pem)和证书文件(client-cert.pem)转换为 PKCS #12 存档:(在数据库服务器端执行命令生成):
openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -name "mysqlclient" -passout pass:123456 -out client-keystore.p12
将客户端密钥和证书导入 Java 密钥库、生成 keystore JSK文件:
keytool -importkeystore -srckeystore client-keystore.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore keystore -deststoretype JKS -deststorepass 123456
jdbcURL:
jdbc:mysql://192.168.1.40:3306/testdb?useSSL=true&requireSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:/home/frank/ssl-cert/truststore&trustCertificateKeyStorePassword=123456&clientCertificateKeyStoreUrl=file:/home/frank/ssl-cert/keystore&clientCertificateKeyStorePassword=123456
该方式连接时, trustore 和 keystore 必须同时存在,否则,如果只有 keystore 而没有 trustore 的话,就会报错如下:
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)
at java.security.Provider$Service.newInstance(Provider.java:1617)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
at javax.net.ssl.SSLContext.getInstance(SSLContext.java:156)
at javax.net.ssl.SSLContext.getDefault(SSLContext.java:96)
at javax.net.ssl.SSLSocketFactory.getDefault(SSLSocketFactory.java:122)
at com.mysql.jdbc.ExportControlled.getSSLSocketFactoryDefaultOrConfigured(ExportControlled.java:159)
at com.mysql.jdbc.ExportControlled.transformSocketToSSLSocket(ExportControlled.java:82)
... 20 more
Caused by: java.security.KeyStoreException: problem accessing trust store
at sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:75)
at javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:250)
at sun.security.ssl.SSLContextImpl$DefaultManagersHolder.getTrustManagers(SSLContextImpl.java:882)
at sun.security.ssl.SSLContextImpl$DefaultManagersHolder.<clinit>(SSLContextImpl.java:854)
at sun.security.ssl.SSLContextImpl$DefaultSSLContext.<init>(SSLContextImpl.java:1027)
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:423)
at java.security.Provider$Service.newInstance(Provider.java:1595)
... 27 more
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:785)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:56)
at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:224)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70)
at java.security.KeyStore.load(KeyStore.java:1445)
at sun.security.ssl.TrustStoreManager$TrustAnchorManager.loadKeyStore(TrustStoreManager.java:372)
at sun.security.ssl.TrustStoreManager$TrustAnchorManager.getTrustedCerts(TrustStoreManager.java:320)
at sun.security.ssl.TrustStoreManager.getTrustedCerts(TrustStoreManager.java:59)
at sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:51)
... 36 more
Caused by: java.security.UnrecoverableKeyException: Password verification failed
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:783)
... 44 more