Skip to content

Why does MySQL say public key retrieval is not allowed?

Last updated

View as Markdown

Quick answer: Your connection has TLS turned off, and the caching_sha2_password plugin will not send the password in clear text. It would use the server’s RSA public key instead, and fetching that key over an unencrypted connection is disabled because a man in the middle could swap it. Turn TLS on and verify the certificate. sslMode=VERIFY_IDENTITY is the setting; REQUIRED only encrypts, and the default, PREFERRED, can fall back to no encryption.

The message comes from MySQL Connector/J and reads:

java.sql.SQLNonTransientConnectionException:
Public Key Retrieval is not allowed

This is an authentication failure, not a network or privilege failure. The wording can send people to firewall rules and grants.

Three facts explain it:

  1. caching_sha2_password is the plugin your account got. It has been the default since 8.0.4, and on a stock mysql:8.4.11 container, 2026-09-20, a plain CREATE USER ... IDENTIFIED BY produced exactly that plugin. The plugin assigned to a new account is governed by authentication_policy, which reads *,, on 8.0.46, 8.4.11 and 9.7.2, where * means the server default.
  2. It refuses to send a password over an unencrypted connection, unless it can encrypt it with the server’s RSA public key first.
  3. The client will not ask the server for that key over an unencrypted connection, because it cannot distinguish the real key from a substitute.

An unencrypted connection is narrower than it sounds. From Caching SHA-2 Pluggable Authentication in the MySQL 8.4 manual, read 2026-09-20: “If the connection is secure, an RSA key pair is unnecessary and is not used. This applies to TCP connections encrypted using TLS, as well as Unix socket-file and shared-memory connections.” A socket connection therefore does not produce this error, which is why the problem reproduces from your application but not from a mysql session inside the container.

The three conditions are this plugin, an insecure transport, and no key the client already trusts. The bench below removes the second and third conditions, and each was enough on its own. It does not remove the first. On a default MySQL 8.4.11 server, 2026-09-20, you cannot use CREATE USER ... IDENTIFIED WITH mysql_native_password because it returns ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded unless the server was started with --mysql-native-password=ON, which is covered at the end of this page.

The bench also shows that the client cannot always remove a condition. On that same MySQL 8.4.11 container, 2026-09-20, with require_secure_transport = ON, an empty cache and --ssl-mode=DISABLED --get-server-public-key returned ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON.

Check the account’s plugin:

SELECT user, host, plugin
FROM mysql.user
WHERE user = 'app_user';

caching_sha2_password there is the first condition.

Then check your connection string for the second. In Connector/J the property is sslMode, and sslMode=DISABLED produces this result. You may also see useSSL=false, which does the same thing and which the vendor marks “DEPRECATED: See ‘sslMode’ property description for details” (Connector/J security properties, read 2026-09-20). They are not equivalent spellings. Keep one sslMode policy and remove the other property instead of setting both.

Older tutorials recommend useSSL=false to silence a certificate warning. That parameter is a common cause of this problem.

Reproduce it from the command line, which gives a clearer error than the Java one. On a stock mysql:8.4.11 container, 2026-09-20, over TCP to an account created with no plugin named, with the authentication cache emptied first:

mysql -h 127.0.0.1 -P 3306 --protocol=TCP -u pk -p --ssl-mode=DISABLED -e "SELECT 1;"
ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password'
reported error: Authentication requires secure connection.

Remove --ssl-mode=DISABLED and the same command returned the row, on that same MySQL 8.4.11 container and day. Asking that session SHOW STATUS LIKE 'Ssl_cipher' reported TLS_AES_128_GCM_SHA256, which was the difference between the two connections.

Two checks do not work on the versions covered here. SHOW VARIABLES LIKE 'have_ssl' returns a row on 8.0.46 and returns nothing at all on 8.4.11 and 9.7.2. default_authentication_plugin is also gone on both of those versions. On 8.4, ask the performance schema instead:

SELECT CHANNEL, PROPERTY, VALUE
FROM performance_schema.tls_channel_status
WHERE PROPERTY = 'Enabled';

On mysql:8.4.11, 2026-09-20, that returned mysql_main / Enabled / Yes.

This is the fix. A server built against OpenSSL generates the TLS and RSA files at first start when auto_generate_certs is on and no SSL options are configured. On a stock mysql:8.4.11 container, 2026-09-20, with nothing configured, auto_generate_certs and caching_sha2_password_auto_generate_rsa_keys both read ON, and the data directory held ca.pem, server-cert.pem, server-key.pem, public_key.pem and private_key.pem. A managed service, hardened image or inherited data directory can differ, so read the variables instead of assuming.

An auto-generated certificate still has to be verified. Distribute it as a trust anchor or replace it. Name the level you want:

# production: encrypted, and the server is who it claims to be
jdbc:mysql://db.example.internal:3306/app?sslMode=VERIFY_IDENTITY
# transitional only: encrypted, endpoint not verified
jdbc:mysql://db.internal:3306/app?sslMode=REQUIRED

The levels are not interchangeable. The difference between the middle two is often misunderstood. From the Connector/J security properties, read 2026-09-20: REQUIRED is “Establish secure connections if the server enabled them, fail otherwise”; VERIFY_CA is “Like REQUIRED but additionally verify the server TLS certificate against the configured Certificate Authority (CA) certificates”; VERIFY_IDENTITY is “Like VERIFY_CA, but additionally verify that the server certificate matches the host to which the connection is attempted.” Thus, VERIFY_CA alone accepts a validly issued certificate for some other hostname. Only VERIFY_IDENTITY checks that it is your server.

REQUIRED stops eavesdropping but does not verify the server’s identity. Treat it as a step toward VERIFY_IDENTITY, not as the final setting.

Do not rely on the default. The Connector/J security properties, read 2026-09-20, give sslMode a default of PREFERRED, documented as “Establish encrypted connections if the server enabled them, otherwise fall back to unencrypted connections”. A connection string with no sslMode at all can therefore use clear text without failing.

From the command line client the same levels are --ssl-mode=REQUIRED, VERIFY_CA and VERIFY_IDENTITY.

Confirm that the connection is encrypted:

SHOW SESSION STATUS LIKE 'Ssl_cipher';

A non-empty value shows that TLS is in use. An empty one means TLS is off, whatever the connection string says.

The flag that works and that you should not use

Section titled “The flag that works and that you should not use”
allowPublicKeyRetrieval=true

This tells the client to accept whatever public key the server offers over the unencrypted connection. The connection then succeeds, which is why this is a common answer.

It removes the protection that the restriction provides. Anything between the client and server can offer its own key, decrypt the password with the matching private key and pass the connection on. Neither the client nor the server detects this.

Loopback to a container on your own machine lowers the risk but does not remove it. Malware, a debug proxy or a logging tool could still observe traffic on the machine, and the connection string may later be used outside the loopback environment. Scope it to a disposable local database, not to “development”.

You can put the server’s public key on the client in advance, with serverRSAPublicKeyFile in Connector/J or --server-public-key-path on the command line. It works. On a stock mysql:8.4.11 container, 2026-09-20, with the authentication cache emptied first, --ssl-mode=DISABLED --server-public-key-path=/var/lib/mysql/public_key.pem connected, while the same command without that one flag and with the cache emptied again returned ERROR 2061. The flag is the only difference between the two.

This protects the password during authentication but does not mean that TLS is unnecessary. The successful connection reported an empty Ssl_cipher. The pinned key does not encrypt your statements, result sets or session, and it does not authenticate the server beyond that exchange. It is an option for authentication in an environment that cannot use TLS, but it is not a replacement for TLS.

Pin the key through a trusted channel. A public key fetched over the same unencrypted connection gives no more protection than allowPublicKeyRetrieval.

These options provide different protections:

settingpassword during authenticationserver identityyour SQL and results
sslMode=VERIFY_IDENTITYprotectedverified, including the hostnameencrypted
sslMode=VERIFY_CAprotectedchain verified, hostname notencrypted
sslMode=REQUIREDprotected from a passive listenernot verifiedencrypted
serverRSAPublicKeyFile, no TLSprotected, if the key was pinned over a trusted channelnot verifiedin clear text
allowPublicKeyRetrieval=true, no TLSnot protected against an active interceptornot verifiedin clear text

Use the first row in production. Use the fourth only where TLS is genuinely impossible and only for the authentication exchange. Use the last only against a database you would be willing to throw away.

Why does it work sometimes and not others?

Section titled “Why does it work sometimes and not others?”

caching_sha2_password has a cache, as its name indicates. Measured on a stock MySQL 8.4.11 container, 2026-09-20, over TCP, one account, in this order:

stepauthentication cacheresult
--ssl-mode=DISABLEDemptied firstERROR 2061
default TLSstill cold for this accountconnected, Ssl_cipher TLS_AES_128_GCM_SHA256
--ssl-mode=DISABLED, immediately afterwarmconnected
--ssl-mode=DISABLED, after FLUSH PRIVILEGESemptied againERROR 2061

The last row explains the third one. The same command fails again once the cache is emptied, so the successful connection used the fast path rather than one that would have worked regardless.

The cache is per account, not per server. Entries are keyed by identity and password: “Entries consist of account-name/password-hash pairs” (Caching SHA-2 Pluggable Authentication, MySQL 8.4 manual, read 2026-09-20). Two accounts on the same server, same container and day: after pk2 authenticated over TLS, pk over --ssl-mode=DISABLED still returned ERROR 2061, while pk2 over --ssl-mode=DISABLED connected. Another user connecting does nothing for you unless they used your account and password.

Changing the password empties that account’s entry too. On the same container and day, with pk warm, an ALTER USER ... IDENTIFIED BY put the next --ssl-mode=DISABLED connection back to ERROR 2061. The same manual page, read 2026-09-20, lists the rest: “It clears the cache entry for any account that is renamed or dropped, or any account for which the credentials or authentication plugin are changed. It empties the cache when the FLUSH PRIVILEGES statement is executed. It empties the cache at server shutdown.”

The intermittency is explained, but it is not a fix. A connection that works because the cache is warm can fail after a restart, and it sends statements in clear text throughout the session.

You can clear it deliberately to test:

FLUSH PRIVILEGES;

The generated connection string is the problem, and it was generated for the environment its training data described. useSSL=false was standard advice for years because Connector/J 5.1 warned about establishing an SSL connection without server identity verification against MySQL 5.7 and other recent servers. The fastest way to silence that warning was to turn SSL off.

A model asked for a MySQL JDBC URL may therefore produce one with TLS disabled. That works with mysql_native_password and fails with the plugin that has been the default since 8.0.

The other common pattern is the proposed fix. Asked to resolve “Public Key Retrieval is not allowed”, the answer that appears often in training data is allowPublicKeyRetrieval=true, because it is the shortest change that makes the error stop.

Grep your configuration for useSSL=false and allowPublicKeyRetrieval, and treat both as findings rather than settings.

Assert Ssl_cipher is non-empty in a startup check, so a connection that falls back to plain text fails visibly.

Keep connectors current. An old client that cannot speak caching_sha2_password reports a different error, usually that the plugin could not be loaded rather than anything about a public key. The fix is the driver, and downgrading the account is a worse answer than it looks. The old plugin is still in the 8.4 server and off: the MySQL 8.4 manual says it “is deprecated as of MySQL 8.0.34, disabled by default in MySQL 8.4, and removed as of MySQL 9.0.0”, and that “the server-side plugin is built into the server, but is disabled by default. To enable it, start the MySQL Server with --mysql-native-password=ON” (manual). The server behaves that way. On MySQL 8.4.11 containers, 2026-09-20, a default server reported PLUGIN_STATUS of DISABLED and CREATE USER ... IDENTIFIED WITH mysql_native_password returned ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded, while the same image started with --mysql-native-password=ON reported it ACTIVE, created the account and let it connect over TCP with --ssl-mode=DISABLED and no key. The reason not to use it is the version after this one: MySQL 9.7.2 refused to start with the same option, logging unknown variable 'mysql-native-password=ON' and aborting. The escape hatch requires a server restart now and blocks an upgrade later.

Use VERIFY_IDENTITY where you can, and VERIFY_CA at the least. Encryption without verification still lets an impersonating server through, which is the risk this error is meant to prevent.

Enforce it on the server, not only in the URL. A connection string can be copied, overridden by a framework or edited by someone in a hurry. require_secure_transport = ON makes a non-TLS connection fail instead of quietly working. On MySQL 8.4.11 in a container, 2026-09-20, with the setting on, all three routes shown here returned ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON.: --get-server-public-key, a pinned --server-public-key-path, and an account whose cache entry was already warm. Turning the setting off and running the same commands again connected, so the rejection comes from the setting rather than the commands.

  • What does 'Public Key Retrieval is not allowed' mean?

    The client is authenticating with caching_sha2_password over a connection with TLS turned off. In that situation the server will only accept the password encrypted with its RSA public key, the client does not have that key, and asking the server for it is disabled by default because a man in the middle could supply a substitute key. So the connection stops.

  • What is the correct fix?

    Turn TLS on, and verify the certificate rather than only encrypting. Measured on a stock MySQL 8.4.11 container, 2026-09-20, the same client and account that failed with --ssl-mode=DISABLED connected with TLS left at its default and reported Ssl_cipher of TLS_AES_128_GCM_SHA256. In Connector/J the property is sslMode, whose default is PREFERRED, and PREFERRED is documented to fall back to an unencrypted connection, so name the level you want rather than relying on the default.

  • Is allowPublicKeyRetrieval=true safe?

    It works and it removes a protection. With it set, the client accepts whatever public key the server hands it over an unencrypted connection, which is the thing an interceptor would exploit. Loopback lowers that particular risk without making it nothing, because the password still crosses a machine that may be compromised and the connection string outlives the loopback it was written for. Treat it as a disposable local development exception.

  • Why did this start after an upgrade?

    Because the account moved from mysql_native_password to caching_sha2_password, usually by being recreated on a newer server. The old plugin never needed the public key, so a connection string with TLS disabled worked for years and then stopped. Going back is a server restart on 8.4 and impossible from 9.0: measured on MySQL 8.4.11 in containers, 2026-09-20, a default server reports mysql_native_password with PLUGIN_STATUS of DISABLED and CREATE USER ... IDENTIFIED WITH mysql_native_password fails with ERROR 1524, while the same image started with --mysql-native-password=ON reports it ACTIVE and creates the account, which then connected over TCP with --ssl-mode=DISABLED and no key at all. MySQL 9.7.2 refused to start with that option, logging unknown variable 'mysql-native-password=ON'. Update the client instead: the switch is a deprecated plugin you would have to remove again before 9.x.

  • Does it affect the command line client too?

    Yes, with a different message. Measured on a stock MySQL 8.4.11 container, 2026-09-20, over a TCP connection with --ssl-mode=DISABLED and an empty authentication cache, the mysql client returns ERROR 2061, 'Authentication plugin caching_sha2_password reported error: Authentication requires secure connection.' The --get-server-public-key flag is its equivalent of allowPublicKeyRetrieval and has the same trade-off. A Unix socket connection never shows this, because the manual counts socket connections as secure.