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

Reply via email to