JDK-8327461 : KeyStore getEntry is not thread-safe
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2024-03-06
  • Updated: 2025-04-28
  • Resolved: 2024-03-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 21 JDK 23
21.0.8Fixed 23 b14Fixed
Related Reports
Relates :  
Description
When the KeyStore is concurrently modified, keyStore.getEntry might fail or return a non-matching private key / certificate pair. This is because PKCS12KeyStore.engineGetEntry calls engineGetKey and engineGetCertificateChain without any synchronization with engineSetEntry.

This is a problem for the PKIX KeyManager (sun.security.ssl.X509KeyManagerImpl), which retrieves the entries from the KeyStore on every handshake. Currently this only impacts the handshakes that are running at the same time as the keystore update, but in JDK-8322767 we are exploring caching the entries on first use, and caching the incorrect entries would affect all handshakes until the next keystore update.

To reproduce:
- Create a PKCS12 key store, and 2 KeyStore.PrivateKeyEntry instances, one with EC key/certificate, one with RSA key/certificate.
- create one thread that calls setEntry in a loop using the same alias, but alternating between the entries:
- in another thread call getEntry in a loop
see the attached reproducer (incomplete, needs keys).

The code quickly fails with the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: private key algorithm does not match algorithm of public key in end entity certificate (at index 0)
	at java.base/java.security.KeyStore$PrivateKeyEntry.<init>(KeyStore.java:552)
	at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetEntry(PKCS12KeyStore.java:1338)
	at java.base/sun.security.util.KeyStoreDelegator.engineGetEntry(KeyStoreDelegator.java:174)
	at java.base/java.security.KeyStore.getEntry(KeyStore.java:1576)


Reproducer:
    public static final String TEST = "test";
    private static void test(KeyStore ks, KeyStore.PrivateKeyEntry ec,
                             KeyStore.PrivateKeyEntry rsa,
                             KeyStore.PasswordProtection protParam)
            throws Exception {
        ks.setEntry(TEST, ec, protParam);

        new Thread(()->{while(true) {
            try {
                ks.setEntry(TEST, ec, protParam);
                ks.setEntry(TEST, rsa, protParam);
            } catch (KeyStoreException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
        }).start();
        while(true) {
            ks.getEntry(TEST, protParam);
        }
    }

Comments
A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk21u-dev/pull/1684 Date: 2025-04-21 08:50:10 +0000
21-04-2025

Fix request [21u] I would like to fix this issue in 21. Low to medium risk. The change is simple, obvious and well tested, but It's in a delicate component. Related issue was opened before this was pushed, so not caused by this change. Clean backport. Test passes. SAP nighlyt testing passed.
21-04-2025

Changeset: ffd43c92 Author: Hai-May Chao <hchao@openjdk.org> Date: 2024-03-11 16:33:17 +0000 URL: https://git.openjdk.org/jdk/commit/ffd43c922e3b8b75a00e494d5484b6a487dd5c90
11-03-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/18156 Date: 2024-03-07 17:06:19 +0000
07-03-2024