昨晚某银行客户咨询他们的MySQL 链接配置的SSL证书无效,在这里简单整理一下,对于MySQL 配置SSL的还并不是很多,默认情况下,MySQL 客户端与服务器之间的通信是明文的。这意味着在网络传输过程中,敏感信息(如用户名、密码、查询数据)极易被窃听或篡改。如果关注传输加密可以为 MySQL 配置 SSL/TLS。
为什么需要 SSL?
简单来说,SSL/TLS 协议通过以下保障安全:
- 加密通信:防止数据在传输过程中被“中间人”窥探。
- 身份验证:通过数字证书,确保客户端连接的是真正的数据库服务器,而非伪造的钓鱼站点。
- 合规要求:等保2.0、GDPR、PCI-DSS等标准都明确要求传输加密
- 混合云/公网场景:数据库暴露在公网或跨云通信时,SSL是基本防线
MySQL原生支持SSL(实际上使用的是TLS协议),无需修改业务SQL,只需在连接时启用SSL选项即可。实际上,目前MySQL使用的就是TLS协议,而不是单纯的SSL协议,TLS(Transport Layer Security)是更为安全的升级版SSL。SSL表示安全套接字层,而TLS表示传输层安全。
生成专属的 SSL 证书
SSL安全证书是由权威认证机构颁发的,是CA机构将公钥和相关信息写入一个文件,CA机构用私钥对公钥和相关信息进行签名后,将签名信息也写入这个文件后生成的一个文件。X.509:是一种证书格式,OpenSSL 相当于SSL的一个实现。
SSL证书文件可以通过Openssl命令手动生成,也可以用MySQL自带提供mysql_ssl_rsa_setup创建, 安装MySQL时会自动创建,但未默认配置生效。
# mysql_ssl_rsa_setup
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'
-----
# mysql_ssl_rsa_setup --datadir=/usr/local/mysql/data --verbose
2026-04-24 15:57:18 [NOTE] Destination directory: /usr/local/mysql-5.7.26-el7-x86_64/data
2026-04-24 15:57:18 [NOTE] Executing : openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017
2026-04-24 15:57:18 [NOTE] Certificate files are present in given dir. Skipping generation.
2026-04-24 15:57:18 [NOTE] RSA key files are present in given dir. Skipping generation.
2026-04-24 15:57:18 [NOTE] Success!
#cd /usr/local/mysql/data
# ls -lrt *pem|cat -n
1 -rw------- 1 root root 1675 Feb 27 16:18 ca-key.pem
2 -rw-r--r-- 1 root root 1107 Feb 27 16:18 ca.pem
3 -rw------- 1 root root 1679 Feb 27 16:18 server-key.pem
4 -rw-r--r-- 1 root root 1107 Feb 27 16:18 server-cert.pem
5 -rw------- 1 root root 1675 Feb 27 16:18 client-key.pem
6 -rw-r--r-- 1 root root 1107 Feb 27 16:18 client-cert.pem
7 -rw------- 1 root root 1675 Feb 27 16:18 private_key.pem
8 -rw-r--r-- 1 root root 451 Feb 27 16:18 public_key.pem`
也可以使用 OpenSSL 手动生成
1. 生成 CA 根证书
# 生成 CA 私钥
openssl genrsa -out ca-key.pem 2048
# 生成 CA 证书 (注意 -days 3650 参数,确保长期有效)
openssl req -new -x509 -nodes -days 3650 \
-key ca-key.pem \
-out ca.pem \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=DBA/CN=MySQL-CA"
2. 生成服务端证书
# 生成服务端私钥
openssl genrsa -out server-key.pem 2048
# 生成证书签名请求 (CSR)
openssl req -new -key server-key.pem \
-out server-req.pem \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=DBA/CN=你的服务器IP或域名"
# 使用 CA 签署服务端证书
openssl x509 -req -in server-req.pem -days 3650 \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem
3. 生成客户端证书
# 生成客户端私钥
openssl genrsa -out client-key.pem 2048
# 生成 CSR 并签署
openssl req -new -key client-key.pem -out client-req.pem \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=DBA/CN=MySQL-Client"
openssl x509 -req -in client-req.pem -days 3650 \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out client-cert.pem
| 文件 | 说明 |
|---|---|
| ca.pem | CA证书 |
| ca-key.pem | CA证书密钥 |
| client-cert.pem | 客户端使用的证书 |
| client-key.pem | 客户端使用的密钥 |
| server-cert.pem | 服务端使用的证书 |
| server-key.pem | 服务端使用的密钥 |
| private_key.pem | 私钥 |
| public_key.pem | 公钥 |
验证证书有效期
# cd /usr/local/mysql/data/
# openssl x509 -in ca.pem -noout -dates
notBefore=Feb 27 08:18:47 2026 GMT
notAfter=Feb 25 08:18:47 2036 GMT
# openssl x509 -in server-cert.pem -noout -dates
notBefore=Feb 27 08:18:47 2026 GMT
notAfter=Feb 25 08:18:47 2036 GMT
数据库内验证
mysql> SHOW STATUS LIKE 'Ssl_server%';
+-----------------------+--------------------------+
| Variable_name | Value |
+-----------------------+--------------------------+
| Ssl_server_not_after | Feb 25 08:18:47 2036 GMT |
| Ssl_server_not_before | Feb 27 08:18:47 2026 GMT |
+-----------------------+--------------------------+
2 rows in set (0.01 sec)
Note: 前提是要mysql配置了SSL
配置 MySQL 服务端
设置文件权限(关键!)
MySQL 对私钥文件的权限要求极其严格。如果权限过大,MySQL 会拒绝读取并报错 Unable to get private key。如果权限不对会启动时提示[ERROR] SSL error: Unable to get private key from ‘server-key.pem’
# 确保 mysql 用户拥有所有权
chown mysql:mysql /var/lib/mysql/*.pem
# 私钥必须设置为 600(仅拥有者可读写),否则 MySQL 启动会失败
chmod 600 /var/lib/mysql/server-key.pem
chmod 644 /var/lib/mysql/server-cert.pem /var/lib/mysql/ca.pem
修改 MySQL 配置文件
编辑 my.cnf(Linux)或 my.ini(Windows)
[mysqld]
ssl-ca=/var/lib/mysql/ca.pem
ssl-cert=/var/lib/mysql/server-cert.pem
ssl-key=/var/lib/mysql/server-key.pem
重启 MySQL 服务生效
验证
mysql> show variables like '%ssl%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| have_openssl | YES |
| have_ssl | YES |
| ssl_ca | ca.pem |
| ssl_capath | |
| ssl_cert | server-cert.pem |
| ssl_cipher | |
| ssl_crl | |
| ssl_crlpath | |
| ssl_key | server-key.pem |
+---------------+-----------------+
9 rows in set (0.01 sec)
如果看到 have_ssl 的值为 YES,说明服务端配置成功。
客户端连接测试
# mysql -uroot -p"anbob.com" --socket=/var/lib/mysql/mysql.sock --ssl-ca=/usr/local/mysql/data/ca.pem --ssl-key=/usr/local/mysql/data/client-key.pem --ssl-cert=/usr/local/mysql/data/client-cert.pem
mysql> status
--------------
mysql Ver 14.14 Distrib 5.7.26, for el7 (x86_64) using EditLine wrapper
Connection id: 3
Current database:
Current user: root@localhost
SSL: Cipher in use is DHE-RSA-AES256-SHA
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.7.26 MySQL Community Server (GPL)
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: latin1
Db characterset: latin1
Client characterset: utf8
Conn. characterset: utf8
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 4 min 28 sec
mysql> SHOW STATUS LIKE 'Ssl_cipher';
+---------------+--------------------+
| Variable_name | Value |
+---------------+--------------------+
| Ssl_cipher | DHE-RSA-AES256-SHA |
+---------------+--------------------+
1 row in set (0.00 sec)
-- 如果不指定ssl
# mysql -uroot -p"anbobxxxxxxx" --socket=/var/lib/mysql/mysql.sock
mysql> status
--------------
mysql Ver 14.14 Distrib 5.7.26, for el7 (x86_64) using EditLine wrapper
Connection id: 2
Current database:
Current user: root@localhost
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.7.26 MySQL Community Server (GPL)
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: latin1
Db characterset: latin1
Client characterset: utf8
Conn. characterset: utf8
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 2 min 2 sec
Threads: 1 Questions: 5 Slow queries: 0 Opens: 105 Flush tables: 1 Open tables: 98 Queries per second avg: 0.040
--------------
mysql> SHOW STATUS LIKE 'Ssl_cipher';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Ssl_cipher | |
+---------------+-------+
1 row in set (0.00 sec)
也可以强制特定用户必须使用 SSL 连接
-- 创建强制 SSL 登录的用户
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
GRANT ALL PRIVILEGES ON *.* TO 'secure_user'@'%';
FLUSH PRIVILEGES;
证书有效期
昨晚客户存在一个问题时,生成的证书时间实际上是+8小时,生效时间是未来时间, 查询到的 ssl_server_not_before 和 ssl_server_not_after 是 MySQL 服务器当前使用的 SSL 证书的有效期。
- 生效时间 (Not Before):
Apr 23 18:21:19 2026 GMT - 过期时间 (Not After):
Apr 20 18:21:19 2036 GMT
GMT 是 Greenwich Mean Time 的缩写,中文称为格林尼治标准时间。是世界时间的“基准点”(位于英国伦敦郊区的皇家格林尼治天文台所在地的时间),其他所有时区都是基于它来计算快慢的。意味着北京时间比格林尼治时间快 8 个小时。可能经常听到 UTC(协调世界时),在日常生活中,你可以认为 GMT 和 UTC 是一样的。
计算机系统为了统一全球不同服务器的时间记录,通常统一使用 GMT/UTC 时间戳来存储时间。SSL 证书标准规定必须使用 UTC/GMT 时间格式记录,以避免全球各地时区混乱。
生成证书时,OpenSSL 默认读取的是服务器的 UTC 时间,生成证书请求或自签名证书时,openssl使用 -startdate 参数强制指定生效时间。时间格式必须是 YYMMDDHHMMSSZ
查看当前时间设置
# timedatectl status
Local time: Fri 2026-04-24 21:09:48 CST
Universal time: Fri 2026-04-24 13:09:48 UTC
RTC time: Fri 2026-04-24 13:09:47
Time zone: Asia/Shanghai (CST, +0800)
NTP enabled: yes
NTP synchronized: yes
RTC in local TZ: no
DST active: n/a
OpenSSL 会考虑时区,如果系统时区设置正确,OpenSSL 读取到当前是“北京时间 22:30”,它会在生成证书前自动进行数学换算:
- 读取系统时间:22:30 (GMT+8)
- 自动减去 8 小时。
- 写入证书:14:30 (GMT)。
如服务器时区设置错误,服务器硬件时钟(RTC)被设置为了 UTC, 所以要提前验证date -u
# date
Fri Apr 24 21:17:47 CST 2026
# date -u
Fri Apr 24 13:17:50 UTC 2026
— over —