Java应用出现 Public Key Retrieval is not allowed 报错的常见原因和解决方法
问题现象
Java 应用在运行过程中突然报java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
错误。
开发童鞋表示不理解,毕竟应用没做任何变更,为什么会突然出现这个错误?
2025-03-31 08:31:11 - create connection SQLException, url: jdbc:mysql://10.0.1.86:3306/information_schema?useSSL=false, errorCode 0, state 08001
java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:448)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1682)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1803)
at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2914)
...
问题原因
用户使用的密码认证插件是 caching_sha2_password 且 JDBC 连接串中指定了useSSL=false
。
当碰到以下场景,就会出现上述报错:
- 第一次连接时。
- 数据库实例发生了重启。
- 服务端执行了 FLUSH PRIVILEGES 操作。
解决方法
解决方法有以下几种:
方案 1
将用户的密码认证插件调整为 mysql_native_password。不推荐该方法,因为 MySQL 9.0 移除了 mysql_native_password。
ALTER USER 'u1'@'%' IDENTIFIED WITH mysql_native_password BY 'MySQL@2025';
方案 2
在 JDBC 连接串中设置useSSL=true
。推荐该方法,但开启 SSL 会有一定的性能开销。
如果既不想开启 SSL,又想避免 Public Key Retrieval is not allowed 错误,以下是两个可选的解决方案:
方案 3
在 JDBC 连接串中添加allowPublicKeyRetrieval=true
。
该方法会自动从 MySQL 服务端获取 RSA 公钥,但这种方法有一定的安全风险,可能会受到中间人攻击。攻击者可以伪造 RSA 公钥,窃取用户密码。
方案 4
在 JDBC 连接串中指定serverRSAPublicKeyFile
。
该方法需要将方案 3 中的公钥内容写到应用侧的一个本地文件中,具体步骤如下:
通过
show status like 'Caching_sha2_password_rsa_public_key'
命令或者从参数 caching_sha2_password_public_key_path(默认是 public_key.pem)指定的文件中获取公钥内容。将公钥内容保存到应用侧的一个本地文件中。
在 JDBC 连接串中指定该公钥文件路径。如,
JDBC_URL = "jdbc:mysql://10.0.1.86:3306/information_schema?useSSL=false&serverRSAPublicKeyFile=/usr/local/caching_sha2_password_public_key.pem"
相较于方案 3,方案 4 无疑会更安全,但在高可用环境下并不适用,因为主从节点的公钥内容通常不同。一旦发生主从切换,JDBC 客户端在重新连接新的主节点时,就可能因公钥不一致而触发 Public Key Retrieval is not allowed 错误。除非在部署时显式配置,让主从节点使用相同的公钥,才能避免该问题。
四种方案的优缺点对比
方案 | 安全等级 | 适用场景 | 备注 |
---|---|---|---|
SSL 加密连接 (useSSL=true) | ★★★★★ | 所有生产环境 | 最安全方案,推荐优先使用 |
手动配置 RSA 公钥 (serverRSAPublicKeyFile) | ★★★★☆ | 禁用 SSL 的生产环境 | 需维护公钥文件 |
自动获取 RSA 公钥 (allowPublicKeyRetrieval=true) | ★★☆☆☆ | 可信内网环境 | 存在中间人攻击风险 |
更改认证插件 | ★☆☆☆☆ | 不推荐 | 兼容性差,安全性低 |
根因分析
简单来说,caching_sha2_password 插件为了加快认证过程,在服务端维护了一个密码哈希缓存。当客户端发起连接时:
- 如果用户的密码哈希已经被缓存,服务端可以直接验证,无需客户端发送明文密码进行验证。
- 如果缓存中没有该用户的密码哈希(比如第一次连接时,除此之外,数据库重启或执行 FLUSH PRIVILEGES,还会清除密码哈希缓存),则客户端需要发送明文密码进行认证。
在发送明文密码时,出于安全考虑,MySQL 要求:
- 要么客户端和服务端之间建立 SSL 加密连接。
- 要么客户端允许通过服务端公钥加密明文密码。
如果两者都不满足,就会抛出 Public Key Retrieval is not allowed 错误。
caching_sha2_password 的认证交互流程
以下是客户端与服务端使用 caching_sha2_password 插件进行身份认证时的完整交互流程:
具体实现细节如下:
MySQL 服务端收到客户端的请求后,会生成一个长度为 21 字节的随机数。
MySQL 服务端给客户端发送一个握手包(handshake packet)。
该握手包是 MySQL 服务端与客户端建立连接时发送的第一个数据包,包的内容如下:
握手包的构造和发送是在
send_server_handshake_packet
函数中实现的。协议版本。
服务端版本。
线程 ID。
随机数的第一部分。
服务端能力标志(低16位) 。
默认字符集编号。
服务端状态标志。
服务端能力标志(高16位) 。
随机数的的长度。
保留字段。
随机数的第二部分。
默认的密码认证插件名称。
在 MySQL 8.4 之前,默认的密码认证插件由 default_authentication_plugin 参数决定。在 MySQL 5.7 中,该参数的默认值为 mysql_native_password,在 MySQL 8.0 中,该参数的默认值为 caching_sha2_password。在 MySQL 8.4 中,移除了这个参数,默认的密码认证插件硬编码为 caching_sha2_password。
客户端在接收到服务端的握手包后,会根据握手包中的密码认证插件进行身份验证。对于 caching_sha2_password 插件,客户端将基于以下公式构造一个
scramble_response
,然后调用prep_client_reply_packet
构造握手包发送给服务端。公式中的 random 是服务端在握手包中发送的随机数。scramble_response = XOR(SHA256(password), SHA256(SHA256(SHA256(password)) + random)
服务端调用
parse_client_handshake_packet
解析客户端返回的握手包,解析出来的内容包括用户名、密码(不是明文密码,是 scramble_response)、库名。基于用户名和客户端 IP 从
ACL
缓存中找到用户对应的认证信息,包括密码认证插件和密码哈希值。调用
Caching_sha2_password::fast_authenticate
进行快速验证。快速验证的逻辑如下:首先基于用户名和主机名构造一个 authorization_id。
判断 authorization_id 是否在 m_cache 存在。m_cache 即密码哈希缓存,是 caching_sha2_password 中的关键组件,它的底层实现是一个哈希表。该哈希表的键是 authorization_id,值是一个二维数组,用来存储新旧两个密码哈希值(从 MySQL 8.0 开始,一个用户可以设置两个密码),存储的值是对密码进行两次 SHA-256 哈希计算的结果,即
SHA256(SHA256(password)
。如果存在,则通过以下步骤验证密码是否正确。
SHA2(m_known, rnd) => scramble_stage1
XOR(scramble, scramble_stage1) => digest_stage1
SHA2(digest_stage1) => digest_stage2
m_known == digest_stage2其中,m_known 是 m_cache 中存储的密码哈希值,即 SHA256(SHA256(password),rnd 是服务端发送给客户端的随机数(random),scramble 是客户端返回给服务端的 scramble_response。
最后,将 m_known 与 digest_stage2 进行比较,如果相等,则意味着密码正确。这个时候,服务端会给客户端发送一个
fast_auth_success
包。如果 authorization_id 在 m_cache 中不存在,或者存在但不匹配,则意味着快速验证失败,这个时候,服务端会给客户端发送一个
perform_full_authentication
包,要求客户端发送密码进行身份验证。客户端收到服务端发送的
perform_full_authentication
包后:如果连接是安全的(即开启了 SSL),则会向客户端发送明文密码。
如果连接不是安全的(即没有开启 SSL,一般是因为客户端显式指定了
--ssl-mode=DISABLED
),则会调用 rsa_init() 初始化 RSA 公钥,这个公钥是 mysql 客户端参数 server-public-key-path 指定的。如果没指定,则看 mysql 客户端参数中是否指定了get-server-public-key。如果既没指定 server-public-key-path,又没指定 get-server-public-key,则 mysql 客户端会提示
ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.
错误。JDBC 中的处理逻辑类似,对于非 SSL 连接,如果既没指定 serverRSAPublicKeyFile,又没指定allowPublicKeyRetrieval,则会提示
Public Key Retrieval is not allowed
错误,具体的实现细节可参考 CachingSha2PasswordPlugin.java 中的 nextAuthenticationStep 方法。如果指定了 get-server-public-key,则客户端会向服务端发送一个
request_public_key
包。服务端收到客户端发送的
request_public_key
包后,会将 RSA 公钥发送给客户端。客户端收到 RSA 公钥后,首先会将密码和服务端之前发送的随机数进行异或运算,然后使用 RSA 公钥对异或后的结果进行加密,最后将加密后的密文发送给服务端。
服务端收到加密后的密文后,会调用 decrypt_RSA_private_key 进行解密,获取明文密码。
服务端 Caching_sha2_password::authenticate 验证密码是否正确。验证的逻辑如下:
从 mysql.user 表 authentication_string 字段中提取迭代次数和盐值。
对于 caching_sha2_password,authentication_string 的格式如下:
分隔符[摘要类型]分隔符[迭代次数]分隔符[盐值][哈希摘要]
其中,分隔符默认是 $,摘要类型是单个字母,A 表示使用 SHA256 算法,迭代次数是 3 位十六进制字符串,默认是 005,乘以 ITERATION_MULTIPLIER(默认是 1000),即为实际迭代次数(默认是 5000 次)。盐值是一个长度为 20 的随机字符串,剩下的字符串即为哈希摘要,长度为 43 字节。
基于提取的迭代次数、盐值和明文密码生成一个哈希摘要。
判断提取的哈希摘要和生成的哈希摘要是否相等,如果相等,则意味着密码正确,否则是错误的。
参考资料
Protocol::HandshakeV10:https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_v10.html
Caching_sha2_password information:https://dev.mysql.com/doc/dev/mysql-server/latest/page_caching_sha2_authentication_exchanges.html
WL#9591: Caching sha2 authentication plugin:https://dev.mysql.com/worklog/task/?id=9591
Java应用出现 Public Key Retrieval is not allowed 报错的常见原因和解决方法的更多相关文章
- mysql遇到java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
在连接数据库的url中,加上allowPublicKeyRetrieval=true from:https://blog.csdn.net/Gushiyuta/article/details/9323 ...
- 关于JDBC连接数据库时出现的Public Key Retrieval is not allowed错误
问题描述 最近在学习MyBatis框架,参考官方的文档通过配置文件的方式已经实现了通过Configuration配置文件和mapper映射文件访问mysql8数据库,于是想试试不使用XML文件去构建S ...
- Jdbc连接MySQL 8时报错“MySQLNonTransientConnectionException: Public Key Retrieval is not allowed”
一.问题 因停电检修,今天重启服务器后,再启动jboss就报错"MySQLNonTransientConnectionException: Public Key Retrieval is n ...
- JDBC链接数据库MySQL 8.0 Public Key Retrieval is not allowed 错误的解决方法
现象 Mybatis和Spring框架整合过程中报 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Publ ...
- MySQL连接提示 public key retrieval is not allowed
使用DBeaver连接mysql数据提示public key retrieval is not allowed 修改DBeaver的驱动属性中的allowPublicKeyRetrieval
- Public Key Retrieval is not allowed
链接MySQL数据库报错: 数据库连接url中添加对应属性的支持.allowPublicKeyRetrieval=true&useSSL=false url: jdbc:mysql://loc ...
- spring boot MySQL Public Key Retrieval is not allowed
建议在链接url处添加对应的属性 jdbc:mysql://localhost:3306/book?allowPublicKeyRetrieval=true&useSSL=false
- Nacos使用 MySQL 8.0 提示Public Key Retrieval is not allowed
原因如下(参考官网给出的连接选项): 如果用户使用了 sha256_password 认证,密码在传输过程中必须使用 TLS 协议保护,但是如果 RSA 公钥不可用,可以使用服务器提供的公钥:可以在连 ...
- Mysql8- Public Key Retrieval is not allowed
在使用 MySQL 8.0 时重启应用后提示 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Public ...
- Mysql8.0 Public Key Retrieval is not allow错误的解决办法
在使用Mysql 8.0时重启后启动项目的事后会报错com.mysql.jdbc.exceptions.jdbc4.MysqlNonTransientConnectionException: Publ ...
随机推荐
- Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
Deepseek卡的问题已经有几周了,还有同学没有解决这个问题? 今天就来教大家如何白嫖阿里云的Deepseek-R1满血版,新用户享受100万token额度,相当的富裕,可以随便用了.并且还配用AP ...
- CF57C Array 题解
发现单调不降序列反过来就是单调不增序列,只需考虑单调不降序列即可. 假如将问题转化为:初始为 \(1\),一共有 \(n+1\) 个位置,有 \(n-1\) 次增加答案的机会,每个位置可以拥有多次增加 ...
- DW - 问题
数据库三范式 1NF(First Normal Form):一个关系模式符合 1NF 的定义,则该关系模式是简单的.简单的意思就是不存在从属或重复的属性,即每个属性都是原子性的. 2NF(Second ...
- FormCreate中在事件中获取api
form-create中在事件中获取api FormCreate 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成组件.支持5个UI框架,并且支持生成任何 Vue 组件. ...
- 使用电阻网络实现的vga驱动电路,fpga驱动vga显示器验证,代替gm7123芯片
之前驱动vga,要么是直接使用fpga管脚直接驱动,颜色为8原色 使用线缆 vs,hs,r,g,b一共五根线,三原色要么是0要么是1,所以色彩最多8种,rgb组合 若要实现真彩色驱动,如rgb888, ...
- Netty基础—5.Netty的使用简介
大纲 1.Netty服务端的启动流程 2.服务端IO事件的处理类 3.Netty客户端的启动流程 4.客户端IO事件的处理类 5.启动Netty服务端和客户端的方法说明 6.Netty服务端和客户端使 ...
- k8s node节点报错 dial tcp 127.0.0.1:8080: connect: connection refused
前言 在搭建好 kubernetes 环境后,master 节点拥有 control-plane 权限,可以正常使用 kubectl. 但其他 node 节点无法使用 kubectl 命令,即使同步过 ...
- 昨晚接收的俄罗斯Meteor-M2气象卫星云图,接收质量还可以!
接收设备: 天馈:自制四臂螺旋天线 硬件:SDRsharp 跟踪:Orbitron.SDRSharpDriverDDE 频率:137.1MHZ 解码:SDRSharp.QPSK.M2_LRPT_Dec ...
- 如何不购买域名在云服务器上搭建HTTPS服务
step 1: 事前准备 step 1.1: 云服务器 购买一台云服务器(带有弹性公网IP),阿里云,腾讯云,华为云什么的都可以. 选择ubuntu系统 开放安全组策略(把你需要的协议/端口暴露出来) ...
- Linux如何从命令行卡死的进程中退出?
Linux如何从命令行卡死的进程中退出? 不知道大家在使用Linux的时候,会不会遇到一些命令,有可能卡顿,有可能执行时间过长(比如使用 find 查找某个文件),这个时候我不想继续执行这个命令了,说 ...