Skip to content

Honor key-store, trust-store and classname of the ssl element - #26153

Open
renatsaf wants to merge 3 commits into
eclipse-ee4j:mainfrom
renatsaf:issue-18175-per-listener-ssl-stores
Open

Honor key-store, trust-store and classname of the ssl element#26153
renatsaf wants to merge 3 commits into
eclipse-ee4j:mainfrom
renatsaf:issue-18175-per-listener-ssl-stores

Conversation

@renatsaf

@renatsaf renatsaf commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #18175

Problem

key-store and trust-store configured in the ssl element of a protocol have never had any effect:

<protocol security-enabled="true" name="ssl-listener">
    <http default-virtual-server="server"><file-cache/></http>
    <ssl key-store="/opt/security/keystore.p12" trust-store="/opt/security/truststore.p12"
         cert-nickname="s1as" classname="com.sun.enterprise.security.ssl.GlassfishSSLImpl"/>
</protocol>

GlassFishSSLContextFactory read the keystore and keystoreType attributes only to log them and
then built the key managers, as well as the trust store, from SSLUtils. SSLUtils delegates to the
SecuritySupport singleton, which opens a single pair of stores in its @PostConstruct from the
javax.net.ssl.keyStore / javax.net.ssl.trustStore system properties and the domain master
password. The store location, type and password of a listener were therefore silently ignored, while
the admin console offers them as editable fields and the config model and asadmin set accept them.

While fixing that I found a second, newer defect. Since SSLImplementation became a HK2 contract,
SSLConfigurator.getSslImplementation() resolves it from the service locator unconditionally and
uses the classname attribute only when no service is registered. GlassfishSSLImpl is a service
and the only implementation on the server classpath, so the attribute is never used. That silently
disables the pluggability documented in http_https.adoc and it also removed the workaround for
this very issue that was suggested in 2012 (clearing classname so that the generic Grizzly
implementation, which does honor the stores, is used).

Solution

Per listener stores. A listener which configures its own store is served by the generic
SSLContextFactory, which loads the store from the configured path, with the configured type and
password. Listeners without their own store keep using the server stores managed by SSLUtils, so
the stores protected by the master password stay the default and nothing changes for a default
domain. The keystore and the truststore are decided independently, so overriding only one of them
works. The store paths themselves cannot carry that decision, because SSLConfigurator merges them
with the system properties of the server before the factory sees them; it therefore marks the stores
which come from the ssl element with the new keystoreConfigured and truststoreConfigured
attributes, and the factory reads that mark.

The classname attribute. It is honored again. The implementation registered in the service
locator is used when the attribute is not set or when it names exactly that implementation, which is
the case of every default domain, so this is not a behaviour change for stock configurations. The
class is loaded by Utils.loadClass, which tries the thread context class loader first.

Tests

GlassFishSSLContextFactoryTest (new; the module had no test sources yet) creates a keystore and a
truststore with KeyTool and configures them as the stores of the listener. The factory is created
with an intentionally empty ServiceLocator, so any fall back to the server stores fails. One of the
tests asks for the s1as alias, which exists only in a server keystore, and asserts that the
resulting IOException names it - that proves the keystore of the listener is really the one which
is opened.

GrizzlyConfigTest.sslWithConfiguredImplementation serves HTTPS from a configuration whose ssl
element names ConfiguredSSLImplementation, a test implementation which is intentionally not a HK2
service and counts its usages.

Both reproducers were verified to fail without the fixes.

EmbeddedAddHttpsListenerTest concatenated the absolute path of the docroot with the absolute path
of the keystore file, so its listener was configured with a key-store which cannot exist. It went
unnoticed because the attribute was ignored; now the listener really tries to open that path and the
client sees a terminated handshake. The listener is configured with the keystore of the test, which
is also the server keystore, as it is in a default domain - the test now covers the configured store
path end to end.

Not included

  • A store of a listener configured without key-store-password still falls back to
    javax.net.ssl.keyStorePassword and then to the default password, which is the existing behaviour
    of SSLConfigurator. Falling back to the master password instead may be more useful, but it would
    mean reaching the security services from grizzly-config, so I left it for a follow up if you
    consider it right.
  • create-ssl still has no parameters for the stores and the documentation describes only the
    classname attribute. Happy to add both once the approach here is agreed.

🤖 Generated with Claude Code

@OndroMih

Copy link
Copy Markdown
Contributor

A test is failing:
EmbeddedAddHttpsListenerTest.test:149->verify:113 » SSLHandshake Remote host terminated the handshake

renatsaf and others added 3 commits August 1, 2026 17:09
The GlassFishSSLContextFactory read the keystore and keystoreType
attributes only to log them and always built the key managers and the
trust store from SSLUtils, which is a singleton opening a single pair of
stores from the javax.net.ssl.keyStore and javax.net.ssl.trustStore
system properties. The key-store, trust-store, their types and their
passwords configured in the ssl element of a protocol were therefore
silently ignored, although the admin console and the config model offer
them.

A listener which configures its own store is now served by the generic
SSLContextFactory, which loads the store from the configured path with
the configured password. Listeners without their own store keep using
the server stores managed with the domain master password.

Fixes eclipse-ee4j#18175

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Since the SSLImplementation became a HK2 contract, SSLConfigurator
resolved it from the service locator and used the classname attribute
only as a fallback for the case when no service was registered. As
GlassfishSSLImpl is a service and the only implementation on the server
classpath, the attribute was never used and a custom SSLImplementation
configured in the domain.xml was silently ignored.

The attribute is honored again; the registered service is used when the
attribute is not set or when it names that service. The class is now
loaded by Utils.loadClass, so the thread context class loader is tried
first.

Refs eclipse-ee4j#18175

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The test concatenated the absolute path of the docroot with the absolute
path of the keystore file, so the key-store of the listener was a path
which cannot exist. It went unnoticed because the attribute was ignored
and the server keystore taken from the javax.net.ssl.keyStore system
property was used instead.

Now that the key-store of the ssl element is honored, the listener fails
to load its keystore and the client sees a terminated handshake. The
listener is configured with the keystore of the test, which is also the
server keystore, as it is in a default domain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@renatsaf
renatsaf force-pushed the issue-18175-per-listener-ssl-stores branch from c8ac1be to 696ebb6 Compare August 1, 2026 14:09
@renatsaf

renatsaf commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, that failure was real and it pointed at two things.

The test itself is broken: EmbeddedAddHttpsListenerTest builds the key store path as
root.getAbsolutePath() + keystore, which concatenates the absolute path of the docroot with the
absolute path of the keystore file. The Windows log shows the result:

Failed to load keystore type PKCS12 with path
D:\a\...\web-api\target\classesC:\Users\RUNNER~1\...\test_keystore.p12

That path has never existed. It went unnoticed because the key-store of the ssl element was
ignored and the server keystore from javax.net.ssl.keyStore was used instead - which is exactly
the defect of #18175. As soon as the attribute is honored, the listener fails to open its keystore
and the client sees the terminated handshake. The listener is now configured with the keystore of
the test, which is also the server keystore, as it is in a default domain.

It is worth saying out loud, because it is the one upgrade visible effect of this PR: a listener
configured with an unusable key-store now fails instead of silently serving the server store. The
failure is logged as SEVERE with the path and the cause.

I also replaced the way the decision is made. Comparing the resolved store path with the path of the
server was a heuristic; SSLConfigurator now marks the stores which come from the ssl element
with the keystoreConfigured / truststoreConfigured attributes and GlassFishSSLContextFactory
reads that mark. It also fixes the case of a listener which configures the same path as the server
but a different password.

Rebased on current main (the nucleus/security/core/pom.xml conflict was the same two test
dependencies added by #26109 in the meantime; I kept theirs). GlassFishSSLContextFactoryTest and
GrizzlyConfigTest pass locally, the embedded test I left to the CI.

@dmatej
dmatej requested a review from OndroMih August 7, 2026 18:47
@dmatej dmatej added this to the 8.0.5 milestone Aug 7, 2026
@dmatej dmatej added the bug Something isn't working label Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The key-store, trust-store element in ssl protocol element are not working

3 participants