The sun.security.ssl.TrustStoreManager.TrustAnchorManager keeps one cached
TrustStoreDescriptor (path, type, provider, password, last-modified time). It
uses that descriptor to validate two caches: the loaded KeyStore (ksRef) and
the extracted trusted certificates (csRef).
getTrustedCerts() replaces the descriptor, ksRef and csRef together when the
descriptor has changed. getKeyStore() replaces the descriptor and ksRef but
leaves csRef alone.
If getTrustedKeyStore() runs after the trust store file was modified, the
cached descriptor is updated while csRef still holds the old certificates. A
following getTrustedCerts() call finds a matching descriptor and returns the
old set.
TrustManagerFactory.init((KeyStore) null) then builds a trust manager that does
not include the new certificates, until the JVM restarts or the weakly
referenced csRef is cleared by the garbage collector.
The only caller of getTrustedKeyStore() in the JDK is
SSLContextImpl.DefaultManagersHolder.getTrustManagers(). It runs when the
default TrustManagerFactory provider is not SunJSSE, so the problem is
reachable with public APIs only.
Steps to Reproduce:
Create a PKCS12 trust store with one certificate and point
javax.net.ssl.trustStore, trustStorePassword and trustStoreType at it.
Build a default trust manager with TrustManagerFactory.getInstance("PKIX",
"SunJSSE") and init((KeyStore) null), and keep a reference to it. It has 1
accepted issuer.
Add a second certificate to the file, making sure its last-modified time
changes.
Register a TrustManagerFactory provider that is not SunJSSE at position 1 (for
example, one that delegates to SunJSSE), and call SSLContext.getDefault().
Build another default SunJSSE trust manager as in step 2.
Results
Expected: The new trust manager has 2 accepted issuers.
Actual: It has 1.
The new test StaleTrustedCerts implements these steps. It fails on an unpatched
21.0.11 and passes with the fix.
Fix
Clear the cached certificates in getKeyStore() together with the other caches:
this.descriptor = descriptor;
this.ksRef = new WeakReference<>(ks);
this.csRef = new WeakReference<>(null);
csRef is private and read only by getTrustedCerts(), which already reloads when
get() returns null.
Testing
Included test
test/jdk/sun/security/ssl/TrustStoreManager/StaleTrustedCerts.java that
reproduces the issue, and verifies the fix.
---------
- [x] I confirm that I make this contribution in accordance with the [OpenJDK
Interim AI Policy](https://openjdk.org/legal/ai).
-------------
Commit messages:
- 8395727: TrustStoreManager keeps stale trusted certificates after the key
store is reloaded
Changes: https://git.openjdk.org/jdk/pull/33545/files
Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=33545&range=00
Issue: https://bugs.openjdk.org/browse/JDK-8395727
Stats: 120 lines in 2 files changed: 120 ins; 0 del; 0 mod
Patch: https://git.openjdk.org/jdk/pull/33545.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/33545/head:pull/33545
PR: https://git.openjdk.org/jdk/pull/33545