MySQL双向SSL认证需先启用服务端SSL:用OpenSSL生成CA及服务器证书,配置my.cnf中ssl_ca、ssl_cert、ssl_key并设require_secure_transport=ON;再创建用户时用REQUIRE SUBJECT/ISSUER绑定客户端证书;客户端连接须同时提供ca.pem、client-cert.pem、client-key.pem且权限为600。

MySQL服务端启用SSL并生成CA和服务器证书
MySQL双向认证的前提是服务端先支持SSL,且拥有由自签名CA签发的服务器证书。不能直接用require_secure_transport=ON就完事——那只是强制加密通道,不校验客户端身份。
关键步骤:用OpenSSL生成根CA密钥和证书,再用该CA为MySQL服务端签发证书(server.crt、server.key),同时确保server.crt里不包含私钥,且权限设为600(MySQL启动时会校验)。
openssl genrsa 2048 > ca-key.pemopenssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca.pemopenssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pemopenssl x509 -req -in server-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
完成后把ca.pem、server-cert.pem、server-key.pem放到MySQL配置指定路径(如/var/lib/mysql/ssl/),并在my.cnf中配置:
[mysqld] ssl_ca = /var/lib/mysql/ssl/ca.pem ssl_cert = /var/lib/mysql/ssl/server-cert.pem ssl_key = /var/lib/mysql/ssl/server-key.pem require_secure_transport = ON
创建需SSL双向认证的MySQL用户并绑定客户端证书
单靠REQUIRE SSL只做单向验证(只验服务端),双向认证必须用REQUIRE X509或更精确的REQUIRE SUBJECT/REQUIRE ISSUER限定客户端证书属性。
例如限制客户端必须使用CN=client1、由指定CA签发:
CREATE USER 'appuser'@'%' IDENTIFIED BY 'p@ssw0rd' REQUIRE SUBJECT '/CN=client1' AND ISSUER '/CN=MyMySQLCA';
注意:SUBJECT值必须与客户端证书实际DN完全一致(区分空格和斜杠顺序),大小写敏感;ISSUER必须匹配ca.pem里的Subject字段。
- 若用
REQUIRE X509,MySQL仅校验证书有效性和签名链,不校验具体字段,安全性较低 - 用户创建后必须执行
FLUSH PRIVILEGES - 不要给root用户配
REQUIRE X509——容易锁死自己
客户端连接时正确加载证书并验证服务端身份
MySQL客户端(如mysql命令行、JDBC、Python mysql-connector)必须同时提供客户端证书+私钥+CA证书,否则服务端拒绝连接。
命令行示例:
mysql -u appuser -p \ --ssl-ca=/path/to/ca.pem \ --ssl-cert=/path/to/client-cert.pem \ --ssl-key=/path/to/client-key.pem \ -h db.example.com
常见失败原因:
- 客户端证书未用同一CA签发 → 报错
SSL connection error: SSL is required and the server doesn't support it(实际是证书链验证失败) -
client-key.pem权限不是600 → MySQL客户端拒绝读取,报SSL connection error: Failed to set certificate - 服务端
ssl_ca路径错误或文件不可读 → 启动日志出现Failed to set up SSL,但mysqld仍可能启动成功,导致后续连接静默失败
JDBC和Python连接中的SSL参数陷阱
JDBC URL里useSSL=true已弃用,必须用sslMode=REQUIRED或更严格的sslMode=VERIFY_IDENTITY;Python的mysql-connector-python默认不校验服务端证书,必须显式传参。
JDBC示例(MySQL Connector/J 8.0+):
jdbc:mysql://db.example.com:3306/mydb? useSSL=false&sslMode=VERIFY_IDENTITY& trustCertificateKeyStoreUrl=file:/path/to/truststore.jks& trustCertificateKeyStorePassword=changeit
Python示例(需提前用keytool或openssl把ca.pem转成JKS,或改用PEM方式):
import mysql.connector
conn = mysql.connector.connect(
host='db.example.com',
user='appuser',
password='p@ssw0rd',
ssl_disabled=False,
ssl_ca='/path/to/ca.pem',
ssl_cert='/path/to/client-cert.pem',
ssl_key='/path/to/client-key.pem'
)
容易忽略的一点:MySQL服务端默认不校验客户端证书的subjectAltName,但如果客户端证书用了SAN(比如IP或DNS名),而服务端配置了require_secure_transport=ON却不配合REQUIRE SUBJECT,就会因字段不匹配被拒,且错误日志里只显示通用的Access denied,没有具体原因。


















