JDBC通过TLS连接MySQL≥8.0.19报bad_certificate错误如何解决
问题根因
MySQL 8.0.19版本引入了多项SSL相关的校验逻辑变更,你的配置正好命中了多个不兼容点:
- 证书序列号唯一性校验:8.0.19及以上版本要求同一CA签发的所有证书序列号必须唯一,你生成服务端、客户端证书时都使用了
-set_serial 01,序列号重复导致服务端校验客户端证书时直接拒绝。 - 服务端私钥权限校验:8.0.19开始会强制检查服务端SSL私钥的文件权限,你配置的
server-key.pem权限为644,权限过宽被判定为不安全,MySQL会自动禁用SSL功能,握手阶段直接返回证书无效错误。 - 证书主机名校验:8.0.19默认开启SSL证书的主机名匹配校验,如果你生成服务端证书时填写的CN(通用名)与你实际连接Docker容器使用的主机名/IP不匹配,且没有配置SAN(主题备用名称)扩展,也会触发校验失败。
- 代码配置错误:你的测试代码里把用户名、密码参数填反了,会额外引发鉴权失败问题。
修复步骤
- 调整证书生成命令,分配唯一序列号,添加SAN扩展(根据你实际连接的地址补充SAN值):
# 原有CA生成逻辑不变 # 生成服务端证书时使用唯一序列号01,添加SAN扩展 openssl req -newkey rsa:2048 -days 3600 \ -nodes -keyout server-key.pem -out server-req.pem \ -subj "/CN=mysql" -addext "subjectAltName = DNS:mysql,DNS:localhost,IP:127.0.0.1" openssl rsa -in server-key.pem -out server-key.pem openssl x509 -req -in server-req.pem -days 3600 \ -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem \ -extfile <(printf "subjectAltName = DNS:mysql,DNS:localhost,IP:127.0.0.1") # 生成客户端证书时使用唯一序列号02 openssl req -newkey rsa:2048 -days 3600 \ -nodes -keyout client-key.pem -out client-req.pem openssl rsa -in client-key.pem -out client-key.pem openssl x509 -req -in client-req.pem -days 3600 \ -CA ca.pem -CAkey ca-key.pem -set_serial 02 -out client-cert.pem
- 修改Dockerfile中私钥的权限配置:
RUN chown mysql:mysql /etc/mysql/my.cnf \ /var/ssl/ \ /var/ssl/server-key.pem \ /var/ssl/ca/ca.pem \ /var/ssl/server-cert.pem && \ chmod 600 /var/ssl/server-key.pem && \ chmod 644 /var/ssl/server-cert.pem \ /var/ssl/ca/ca.pem
- 修复测试代码中的用户密码配置错误:
properties.put("user", mySQLContainerSSL.getUsername()); properties.put("password", mySQLContainerSSL.getPassword());
- 可选(测试环境临时绕过校验,生产环境不推荐):如果不需要校验主机名,可在JDBC连接参数中添加配置:
properties.put("disabledSslHostnameVerification", "true");
如果需要严格校验,需把CA证书导入客户端truststore,并补充配置:
properties.put("trustCertificateKeyStoreUrl", "你的truststore文件路径"); properties.put("trustCertificateKeyStorePassword", "你的truststore密码");
内容的提问来源于stack exchange,提问作者Yannick Weber
相关产品推荐
相关产品推荐

