MySQL SSL 认证配置实战

昨晚某银行客户咨询他们的MySQL 链接配置的SSL证书无效,在这里简单整理一下,对于MySQL 配置SSL的还并不是很多,默认情况下,MySQL 客户端与服务器之间的通信是明文的。这意味着在网络传输过程中,敏感信息(如用户名、密码、查询数据)极易被窃听或篡改。如果关注传输加密可以为 MySQL 配置 SSL/TLS。

为什么需要 SSL?

简单来说,SSL/TLS 协议通过以下保障安全:

  1. 加密通信:防止数据在传输过程中被“中间人”窥探。
  2. 身份验证:通过数字证书,确保客户端连接的是真正的数据库服务器,而非伪造的钓鱼站点。
  3. 合规要求:等保2.0、GDPR、PCI-DSS等标准都明确要求传输加密
  4. 混合云/公网场景:数据库暴露在公网或跨云通信时,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.pemCA证书
ca-key.pemCA证书密钥
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”,它会在生成证书前自动进行数学换算:

  1. 读取系统时间:22:30 (GMT+8)
  2. 自动减去 8 小时。
  3. 写入证书: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 —

Leave a Comment