Hello,
I am currently working on a migration from Wildfly 17 to Wildfly 26, on Java 11. Previously for authentication we were using a custom LoginModule with picketbox. To replace this, I have created the following classes:
DatawaveHttpAuthenticationMechanismFactory. This implements HttpServerAuthenticationMechanismFactory and returns an instance of DatawaveHttpAuthenticationMechanism when mechanism name is DATAWAVE-AUTH.
DatawaveHttpAuthenticationMechanism. This implements HttpAuthenticationMechanism, and will attempt to obtain one of the following pieces of evidence to verify via a callback:
JWTEvidence: custom Evidence class that wraps around a JSON Web Token provided via a header value in the HttpServerRequest.
TrustedHeaderEvidence: custom Evidence class that wraps around user identification info provided via header values in the HttpServerRequest.
ProxiedX509CertificateEvidence: custom Evidence class that wraps around proxied information provided via headers in the HttpServerRequest, and an X509Certificate that is obtained from the certificate chain returned by HttpServerRequest.getPeerCertificates().
DatawaveSecurityRealm: This implements CacheableSecurityRealm, and will return a RealmIdentity if given a valid piece of Evidence from above.
Snippet from DatawaveAuthenticationMechanism showing the logic around obtain X509 certs from the HttpServerRequest:
/**
* Attempt to obtain and return a piece of {@link ProxiedX509CertificateEvidence} that wraps around a user's certificate obtained from the request's peer
* certificate chain.
* @param request the request
* @param proxiedEntities the proxied entities header value
* @param proxiedIssuers the proxied issuers header value
* @return the evidence if a certificate can be found, or null otherwise
*/
private ProxiedX509CertificateEvidence getProxiedSSLEvidence(HttpServerRequest request, String proxiedEntities, String proxiedIssuers) {
if (request.getSSLSession() != null) {
log.trace("Loading peer certificates");
Certificate[] peerCertificates = request.getPeerCertificates();
if (log.isTraceEnabled()) {
log.trace("request.getPeerCertificates()=" + peerCertificates);
}
X509Certificate[] x509Certificates = X500.asX509CertificateArray(peerCertificates);
X509Certificate certificate = x509Certificates[0];
return new ProxiedX509CertificateEvidence(certificate, proxiedEntities, proxiedIssuers);
} else {
log.trace("Request does not have SSLSession");
}
return null;
}
Below are the relevant sections configuring security for Wildfly as a standalone deployment:
#
# Configure SSL
#
# Create the server key store.
/subsystem=elytron/key-store=serverKeyStore:add( \
path=${KEYSTORE}, \
credential-reference={clear-text="${KEYSTORE_PASSWORD}"}, \
type=${KEYSTORE_TYPE})
# Create the server key manager.
/subsystem=elytron/key-manager=serverKeyManager:add( \
key-store=serverKeyStore, \
credential-reference={clear-text="${KEYSTORE_PASSWORD}"})
# Create the server trust store.
/subsystem=elytron/key-store=serverTrustStore:add( \
path=${TRUSTSTORE}, \
credential-reference={clear-text="${TRUSTSTORE_PASSWORD}"}, \
type=${TRUSTSTORE_TYPE})
# Create the server trust manager.
/subsystem=elytron/trust-manager=serverTrustManager:add( \
key-store=serverTrustStore)
# Create the server SSL context. The protocols "TLSv1.1" and "TLSv1.2" will be supported, a client certificate will be required on an SSL handshake, and the
# security-domain 'datawaveElytron' will be used for authentication during the SSL session establishment.
/subsystem=elytron/server-ssl-context=serverSSLContext:add( \
key-manager=serverKeyManager, \
trust-manager=serverTrustManager, \
protocols=["TLSv1.1","TLSv1.2"], \
want-client-auth=true, \
security-domain=datawaveElytron)
#
# Configure datawave security domain (elytron)
#
# Create a custom role decoder that will decode the roles a user has from a DatawavePrincipal.
/subsystem=elytron/custom-role-decoder=datawaveRoleDecoder:add( \
class-name=datawave.security.realm.RequiredRoleDecoder, \
module="datawave.security", \
configuration={})
# Create a custom realm that handles preparing the DatawavePrincipal for authentication.
/subsystem=elytron/custom-realm=datawaveRealm:add( \
class-name=datawave.security.realm.DatawaveSecurityRealm, \
module="datawave.security", \
configuration={ \
certVerifier="datawave.security.cert.DatawaveCertVerifier", \
ocspLevel="off", \
trustedHeadersEnabled="${dw.trusted.header.authentication:false}" \
})
# Create and configure the security domain 'datawaveElytron' that will handle the authentication and authorization.
/subsystem=elytron/security-domain=datawaveElytron:add(default-realm=datawaveRealm,realms=[{realm=datawaveRealm}],role-decoder=datawaveRoleDecoder)
#
# Configure the HTTP/HTTPS listener for undertow
#
# Switch https-listener from the legacy security-realm to the Elytron server ssl context.
/subsystem=undertow/server=default-server/https-listener=https/:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=ssl-context,value=serverSSLContext)
#
# Configure the authentication mechanism for Elytron.
#
/subsystem=elytron/service-loader-http-server-mechanism-factory=datawaveMechanismFactory:add( \
module=datawave.security)
/subsystem=elytron/http-authentication-factory=datawaveAuthenticationFactory:add( \
http-server-mechanism-factory=datawaveMechanismFactory, \
security-domain=datawaveElytron, \
mechanism-configurations=[{mechanism-name=DATAWAVE-AUTH,mechanism-realm-configurations=[{realm-name=datawave}]}])
#
# Ensure undertow is configured to use the custom authentication mechanism.
#
# Configure a security domain for the application that uses the datawave mechanism factory. Override the deployment
# config in order to support the use of DATAWAVE-AUTH instead of BASIC only by default.
/subsystem=undertow/application-security-domain=datawave:add( \
http-authentication-factory=datawaveAuthenticationFactory, \
override-deployment-config=true)
# Ensure the default application security domain is 'datawave'.
/subsystem=undertow:write-attribute( \
name=default-security-domain, value="datawave")
#
# EJB subsystem configuration
#
# Map the 'datawaveElytron' security domain to the name 'datawave' and make it discoverable by EJBs.
/subsystem=ejb3/application-security-domain=datawave:add(security-domain=datawaveElytron)
Currently the issue is that when Undertow is configured to use the server-ssl-context via
/subsystem=undertow/server=default-server/https-listener=https/:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=ssl-context,value=serverSSLContext)
And a GET is done on one of our resource paths with a PKI cert loaded in the browser, Wildfly will create a Principal from the SSL cert, but will not trigger the creation of the custom authentication mechanism, nor delegate to the custom security realm. The logs show:
2026-02-12 18:38:20,937 TRACE [org.wildfly.security.tls] (default I/O-2) Evaluating filter "add not (encryption is one of (NULL)), then remove fully any of (authentication is one of (NULL), encryption is one of (NULL), export cipher suite is true, openssl security level is one of (LOW), protocol is one of (SSLv2))" on supported mechanisms:
TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
TLS_RSA_WITH_NULL_SHA256
TLS_ECDHE_ECDSA_WITH_NULL_SHA
TLS_ECDHE_RSA_WITH_NULL_SHA
SSL_RSA_WITH_NULL_SHA
2026-02-12 18:38:21,051 TRACE [org.wildfly.security] (default task-1) X550Name.asX500Principal() is not available.: java.lang.IllegalAccessException: access to public member failed: sun.security.x509.X500Name.asX500Principal[Ljava.lang.Object;@23b657cb/invokeVirtual, from public Lookup
at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:942)
at java.base/java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:2201)
at java.base/java.lang.invoke.MethodHandles$Lookup.checkMethod(MethodHandles.java:2141)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:2285)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:2278)
at java.base/java.lang.invoke.MethodHandles$Lookup.unreflect(MethodHandles.java:1747)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.x500.util.X500PrincipalUtil.<clinit>(X500PrincipalUtil.java:58)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.ssl.SecurityDomainTrustManager.doClientTrustCheck(SecurityDomainTrustManager.java:85)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.ssl.SecurityDomainTrustManager.checkClientTrusted(SecurityDomainTrustManager.java:71)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkClientCerts(CertificateMessage.java:682)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:411)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:375)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1076)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1063)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1010)
at io.under...@2.2.19.Final//io.undertow.protocols.ssl.SslConduit$5.run(SslConduit.java:1136)
at org.jbos...@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at org.jbo...@3.8.7.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
at java.base/java.lang.Thread.run(Thread.java:829)
2026-02-12 18:38:21,051 TRACE [org.wildfly.security] (default task-1) Principal assigning: [CN=Test A. User, OU=Example Developers, O=Example Corp, C=US], pre-realm rewritten: [CN=Test A. User, OU=Example Developers, O=Example Corp, C=US], realm name: [datawaveRealm], post-realm rewritten: [CN=Test A. User, OU=Example Developers, O=Example Corp, C=US], realm rewritten: [CN=Test A. User, OU=Example Developers, O=Example Corp, C=US]
Conversely, when the following lines are not included in the configuration file:
/subsystem=undertow/server=default-server/https-listener=https/:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=ssl-context,value=serverSSLContext)
then the authentication request is routed through the custom authentication mechanism, but no peer certificates are loaded in the HttpServerRequest, as shown by the logs below:
2026-02-12 19:57:43,585 TRACE [org.wildfly.security.tls] (default I/O-2) Evaluating filter "add not (encryption is one of (NULL)), then remove fully any of (authentication is one of (NULL), encryption is one of (NULL), export cipher suite is true, openssl security level is one of (LOW), protocol is one of (SSLv2))" on supported mechanisms:
TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
TLS_RSA_WITH_NULL_SHA256
TLS_ECDHE_ECDSA_WITH_NULL_SHA
TLS_ECDHE_RSA_WITH_NULL_SHA
SSL_RSA_WITH_NULL_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Dropping unknown mechanism TLS_AES_256_GCM_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Dropping unknown mechanism TLS_AES_128_GCM_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Dropping unknown mechanism TLS_CHACHA20_POLY1305_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_AES_256_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_DHE_RSA_WITH_AES_128_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_AES_256_GCM_SHA384
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_AES_128_GCM_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_AES_256_CBC_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_AES_128_CBC_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_AES_256_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_AES_128_CBC_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Dropping unknown mechanism TLS_EMPTY_RENEGOTIATION_INFO_SCSV
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_RSA_WITH_NULL_SHA256
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_ECDSA_WITH_NULL_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism TLS_ECDHE_RSA_WITH_NULL_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Found supported mechanism SSL_RSA_WITH_NULL_SHA
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384/ECDHE-ECDSA-AES256-GCM-SHA384 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256/ECDHE-ECDSA-AES128-GCM-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256/ECDHE-ECDSA-CHACHA20-POLY1305 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384/ECDHE-RSA-AES256-GCM-SHA384 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256/ECDHE-RSA-CHACHA20-POLY1305 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256/ECDHE-RSA-AES128-GCM-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_AES_256_GCM_SHA384/DHE-RSA-AES256-GCM-SHA384 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256/DHE-RSA-CHACHA20-POLY1305 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256/DHE-RSA-AES128-GCM-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384/ECDHE-ECDSA-AES256-SHA384 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384/ECDHE-RSA-AES256-SHA384 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256/ECDHE-ECDSA-AES128-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256/ECDHE-RSA-AES128-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_AES_256_CBC_SHA256/DHE-RSA-AES256-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256/DHE-RSA-AES128-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA/ECDHE-ECDSA-AES256-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA/ECDHE-RSA-AES256-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA/ECDHE-ECDSA-AES128-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA/ECDHE-RSA-AES128-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_AES_256_CBC_SHA/DHE-RSA-AES256-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_DHE_RSA_WITH_AES_128_CBC_SHA/DHE-RSA-AES128-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_RSA_WITH_AES_256_GCM_SHA384/AES256-GCM-SHA384 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_RSA_WITH_AES_128_GCM_SHA256/AES128-GCM-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_RSA_WITH_AES_256_CBC_SHA256/AES256-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_RSA_WITH_AES_128_CBC_SHA256/AES128-SHA256 due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_RSA_WITH_AES_256_CBC_SHA/AES256-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Adding cipher suite TLS_RSA_WITH_AES_128_CBC_SHA/AES128-SHA due to add rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Fully removing cipher suite TLS_RSA_WITH_NULL_SHA256/NULL-SHA256 due to full remove rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Fully removing cipher suite TLS_ECDHE_ECDSA_WITH_NULL_SHA/ECDHE-ECDSA-NULL-SHA due to full remove rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Fully removing cipher suite TLS_ECDHE_RSA_WITH_NULL_SHA/ECDHE-RSA-NULL-SHA due to full remove rule
2026-02-12 19:57:43,586 TRACE [org.wildfly.security.tls] (default I/O-2) Fully removing cipher suite TLS_RSA_WITH_NULL_SHA/NULL-SHA due to full remove rule
2026-02-12 19:57:43,780 WARN [org.wildfly.extension.elytron] (default task-1) WFLYELY01085: Generated self-signed certificate at /opt/datawave/contrib/datawave-quickstart/bin/services/datawave/wildfly-install/standalone/configuration/application.keystore. Please note that self-signed certificates are not secure and should only be used for testing purposes. Do not use this self-signed certificate in production.
SHA-1 fingerprint of the generated key is 6a:30:47:12:88:75:3d:ff:dc:84:69:4d:d1:6d:d7:e6:09:be:53:5e
SHA-256 fingerprint of the generated key is 35:23:6a:f9:7f:79:68:4e:f2:f1:c4:f5:88:6c:cf:52:45:0b:ad:49:74:67:76:95:71:76:32:a6:d0:55:cf:83
2026-02-12 19:57:43,780 TRACE [org.wildfly.extension.elytron] (default task-1) saving KeyStore to the file [/opt/datawave/contrib/datawave-quickstart/bin/services/datawave/wildfly-install/standalone/configuration/application.keystore]
2026-02-12 19:57:43,859 TRACE [org.wildfly.security.http.servlet] (default task-1) Created ServletSecurityContextImpl enableJapi=true, integratedJaspi=true, applicationContext=default-host /DataWave
2026-02-12 19:57:43,859 TRACE [org.wildfly.security.http.servlet] (default task-1) No AuthConfigProvider for layer=HttpServlet, appContext=default-host /DataWave
2026-02-12 19:57:43,859 TRACE [org.wildfly.security.http.servlet] (default task-1) JASPIC Unavailable, using HTTP authentication.
2026-02-12 19:57:43,861 TRACE [org.wildfly.security] (default task-1) No CachedIdentity to restore.
2026-02-12 19:57:43,862 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanismFactory] (default task-1) createAuthenticationMechanism(), mechanismName=DATAWAVE-AUTH, properties={org.wildfly.security.http.realm=DATAWAVE Web Services, org.wildfly.security.http.context-path=/DataWave}, callbackHandler=org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$SecurityIdentityCallbackHandler@906bc9c
2026-02-12 19:57:43,866 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) constructor(), subjectDnHeader='X-SSL-ClientCert-Subject', issuerDnHeader='X-SSL-ClientCert-Issuer', jwtEnabled=false, trustedHeadersEnabled=false, dnsToPrune=null
2026-02-12 19:57:43,866 TRACE [org.wildfly.security] (default task-1) Created HttpServerAuthenticationMechanism [org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$1@173b39a4] for mechanism [DATAWAVE-AUTH]
2026-02-12 19:57:43,866 TRACE [org.wildfly.security] (default task-1) Handling SocketAddressCallback
2026-02-12 19:57:43,867 TRACE [org.wildfly.security] (default task-1) Handling MechanismInformationCallback type='HTTP' name='DATAWAVE-AUTH' host-name='ec2-98-90-54-112.compute-1.amazonaws.com' protocol='https'
2026-02-12 19:57:43,867 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) evaluateRequest(), requestPath=/Query/EdgeQuery/createAndNext
2026-02-12 19:57:43,869 TRACE [org.wildfly.security.http.cert] (default task-1) loading from cache: null
2026-02-12 19:57:43,869 TRACE [org.wildfly.security.http.cert] (default task-1) loading from cache: null
2026-02-12 19:57:43,869 TRACE [org.wildfly.security] (default task-1) Handling CachedIdentityAuthorizeCallback: principal = null authorizedIdentity = null
2026-02-12 19:57:43,869 TRACE [org.wildfly.security.http.cert] (default task-1) clearing identity cache
2026-02-12 19:57:43,869 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) Identity was authorized by CachedIdentityAuthorizeCallback handler: false
2026-02-12 19:57:43,869 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) Authenticating with proxiedEntities=null and proxiedIssuers=null
2026-02-12 19:57:43,870 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) Loading peer certificates
2026-02-12 19:57:43,870 TRACE [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) request.getPeerCertificates()=null
2026-02-12 19:57:43,870 ERROR [datawave.security.auth.DatawaveHttpAuthenticationMechanism] (default task-1) Failed to get peer certs from ssl session info: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
at java.base/sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:560)
at datawave.security//datawave.security.auth.DatawaveHttpAuthenticationMechanism.getProxiedSSLEvidence(DatawaveHttpAuthenticationMechanism.java:295)
at datawave.security//datawave.security.auth.DatawaveHttpAuthenticationMechanism.getEvidence(DatawaveHttpAuthenticationMechanism.java:249)
at datawave.security//datawave.security.auth.DatawaveHttpAuthenticationMechanism.attemptAuthentication(DatawaveHttpAuthenticationMechanism.java:139)
at datawave.security//datawave.security.auth.DatawaveHttpAuthenticationMechanism.evaluateRequest(DatawaveHttpAuthenticationMechanism.java:107)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.http.util.SetMechanismInformationMechanismFactory$1.evaluateRequest(SetMechanismInformationMechanismFactory.java:119)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.http.util.SocketAddressCallbackServerMechanismFactory$1.evaluateRequest(SocketAddressCallbackServerMechanismFactory.java:82)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$1.evaluateRequest(SecurityIdentityServerMechanismFactory.java:85)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.http.HttpAuthenticator$AuthenticationExchange.authenticate(HttpAuthenticator.java:325)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.http.HttpAuthenticator$AuthenticationExchange.access$800(HttpAuthenticator.java:300)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.http.HttpAuthenticator.authenticate(HttpAuthenticator.java:94)
at org.wildfly.security.ely...@1.10.1.Final//org.wildfly.elytron.web.undertow.server.SecurityContextImpl.authenticate(SecurityContextImpl.java:107)
at org.wildfly.security.elytron...@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.ServletSecurityContextImpl.authenticate(ServletSecurityContextImpl.java:115)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:55)
at io.under...@2.2.19.Final//io.undertow.server.handlers.DisableCacheHandler.handleRequest(DisableCacheHandler.java:33)
at io.under...@2.2.19.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.under...@2.2.19.Final//io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:53)
at io.under...@2.2.19.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:59)
at io.under...@2.2.19.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at org.wildfly.security.elytron...@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
at io.under...@2.2.19.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.under...@2.2.19.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at io.under...@2.2.19.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:275)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:79)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:134)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:131)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:255)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:79)
at io.undert...@2.2.19.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:100)
at io.under...@2.2.19.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
at io.under...@2.2.19.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:852)
at org.jbos...@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at org.jbo...@3.8.7.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
at java.base/java.lang.Thread.run(Thread.java:829)
How can I ensure that the peer certificates are loaded in the HttpServerRequest? Is the server-ssl-context the appropriate component to use in this context, or do I need to use something else?
Any feedback would be greatly appreciated.
Thank you,
Laura
--
You received this message because you are subscribed to a topic in the Google Groups "WildFly" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/wildfly/5i4LAvX86y0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to wildfly+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/wildfly/9c411775-548b-4383-b18c-4a42dbee35abn%40googlegroups.com.
Hi Laura,
thank you for the detailed description, it’s very helpful to frame the issue.
From the logs it looks like the TLS/SSL layer and client-auth are working correctly and the client certificate is reaching Elytron, but the custom HttpServerAuthenticationMechanism is never actually involved. In WildFly 26 the extraction of the client certificate for mapping into the SecurityDomain happens in the SecurityDomainTrustManager/SSL layer, before Undertow has a chance to invoke the HTTP mechanism, so the fact that a Principal is created from the client certificate does not necessarily mean your DatawaveHttpAuthenticationMechanism will be executed.
To be able to inspect request.getPeerCertificates() inside your mechanism, I’d suggest checking/considering the following points:
Make sure that your DATAWAVE-AUTH is actually the selected mechanism for that request (for example by configuring a mapping pattern on the application-security-domain, or by making mechanism-name the only mechanism available for that deployment).
If mTLS is only supposed to provide the client certificate to your custom realm, you might delegate TLS authentication entirely to Elytron (as you already do with serverSSLContext and SecurityDomainTrustManager) and have your realm use the Principal/credentials provided by Elytron directly, instead of calling getPeerCertificates() again on the HttpServerRequest.
The IllegalAccessException on X500Name.asX500Principal() points to a reflective-access issue with Java 11; you can work around it by adding the Elytron module to the JVM startup options with the appropriate --add-opens for the sun.security.x509 package, or by upgrading to a WildFly/Elytron version that already includes a fix for this reflective access.
If you can also share the deployment configuration snippet (web.xml / jboss-web.xml, or the specific application-security-domain mapping for this app), I can try to propose an exact configuration to ensure your HttpServerAuthenticationMechanism is actually invoked in the authentication chain.
You received this message because you are subscribed to the Google Groups "WildFly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wildfly+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/wildfly/CAC0CKwQuRLKuEF3cCtQiUzEGoy9faTxKp5pQPd3BCqXefusWxA%40mail.gmail.com.
Hi,
Thank you for the quick response. See below for relevant snippets from several different configuration files:
We have several web.xml files that configure access to our APIs. In all of them, the auth method used is configured as follows:
<!--
Use the custom DATAWAVE authentication mechanism. This mechanism handles the X-ProxiedEntitiesChain/X-ProxiedIssuersChain headers
and is also set up to handle "trusted header" authentication where a load balancer terminates the SSL session and passes the
client certificate information along in controlled headers.
-->
<login-config>
<auth-method>DATAWAVE-AUTH</auth-method>
<realm-name>DATAWAVE Web Services</realm-name>
</login-config>
This configuration has not been updated since our legacy security config. Should the realm-name property be set to the application security domain datawave? Or perhaps the underlying security domain datawaveElytron that directly refers to the custom security realm?
We have several jboss-web.xml files that are all configured to use the security-domain datawave.
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_8_0.xsd"
version="8.0">
<security-domain>datawave</security-domain>
</jboss-web>
We have a number of jboss-ejb3.xml files scattered throughout our sub-projects. They all refer to the security-domain datawave.
<?xml version="1.0" encoding="UTF-8"?>
<jboss:jboss
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns:s="urn:security:1.1"
version="3.1" impl-version="2.0">
<assembly-descriptor>
<!-- Configure all beans in this EJB jar to use the "datawave" security domain -->
<s:security>
<ejb-name>*</ejb-name>
<s:security-domain>datawave</s:security-domain>
</s:security>
</assembly-descriptor>
</jboss:jboss>
We start Wildfly with the standalone-full.xml file. I have attached two versions of standalone-full.xml, one where the undertow https listener was configured to use the serverSSLContext, and one where it was not.
To respond to the points you brought up:
“Make sure that your DATAWAVE-AUTH is actually the selected mechanism for that request (for example by configuring a mapping pattern on the application-security-domain, or by making mechanism-name the only mechanism available for that deployment).”
Could you provide some examples of how this would be done? Or link to some documentation covering this?
“If mTLS is only supposed to provide the client certificate to your custom realm, you might delegate TLS authentication entirely to Elytron (as you already do with serverSSLContext and SecurityDomainTrustManager) and have your realm use the Principal/credentials provided by Elytron directly, instead of calling getPeerCertificates() again on the HttpServerRequest.”
We need the X509Certificate from the peer certificate chain in order to do some additional validation on it within the security realm before using it to fetch a user’s information from our underlying user identity store. Would we be able to fetch the X509Certificate from the Principal that’s provided by the SecurityDomainTrustManager?
“The IllegalAccessException on X500Name.asX500Principal() points to a reflective-access issue with Java 11; you can work around it by adding the Elytron module to the JVM startup options with the appropriate --add-opens for the sun.security.x509 package, or by upgrading to a WildFly/Elytron version that already includes a fix for this reflective access.”
I attempted to do this by adding the following to the standalone.conf file:
# Allow reflective access to the sun.security.x509 package.
JAVA_OPTS="$JAVA_OPTS --add-opens java.base/sun.security.x509=ALL_UNNAMED"
And verified that the JAVA_OPTS variable was updated in the wildfly deployment via jboss-cli.sh:
[standalone@localhost:9990 /] :resolve-expression(expression="${JAVA_OPTS}")
{
"outcome" => "success",
"result" => " -server -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8787 -Duser.timezone=GMT -Df
ile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true --add-opens java.base/sun.security.x509=ALL_UNNAMED --add-exports=
java.desktop/sun.awt=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports=java.naming/com
.sun.jndi.url.ldap=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.url.ldaps=ALL-UNNAMED --add-opens=java.base/java
.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAM
ED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/jav
a.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.management/javax.management
=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED "
}
But I am still getting the IllegalAccessException:
2026-02-13 19:00:08,507 TRACE [org.wildfly.security] (default task-1) X550Name.asX500Principal() is not available.: java.lang.IllegalAccessException: access to public member failed: sun.security.x509.X500Name.asX500Principal[Ljava.lang.Object;@78b6c0f2/invokeVirtual, from public Lookup
We’re currently targeting an upgrade to Wildfly 26 so that we can use Jakarta EE 8 until we’re ready to tackle a migration to Jakarta EE 10, so upgrading to a later version of Wildfly where the reflective access was fixed won’t work for us for now.
Please let me know if you need any additional info/snippets.
Thank you,
Laura
Hi Laura,
thank you for the additional details and the configuration snippets, they are very helpful.
Regarding your questions about selecting DATAWAVE-AUTH as the active mechanism: in Elytron this is usually done either by using an http-authentication-factory where your DatawaveHttpAuthenticationMechanism is the only configured mechanism for that application, or by using mechanism-configurations with mechanism-name="DATAWAVE-AUTH" and an appropriate match-rules section that targets your deployment (for example by application-context or virtual-host). In practice this means defining an application-security-domain that points to that http-authentication-factory, and then referencing that application-security-domain from your deployment (via jboss-web.xml or elytron-annotations). If you like, I can propose an example stanza for the application-security-domain and http-authentication-factory based on how you are currently configuring datawave / datawaveElytron.
About using the certificate from the Principal: if TLS client authentication is terminated by Elytron’s SecurityDomainTrustManager, the resulting Principal will typically be an X500Principal built from the client certificate’s subject. You will not automatically get the full certificate chain from the Principal alone, but you can either:
expose the X509Certificate (or chain) as a credential in your custom realm (for example by writing a small Elytron RealmMapper/SecurityRealm that stores it as an X509CertificateChainCredential), or
keep accessing the certificate at the SSL layer and pass it into your realm as part of the identity creation.
So, yes, you can base your additional validation on information derived from the certificate that Elytron already used, but you may need a small glue layer to make the chain available inside the realm in the exact form you need.
Concerning the IllegalAccessException on X500Name.asX500Principal(): the --add-opens java.base/sun.security.x509=ALL_UNNAMED JVM option you added is correct in principle, but Elytron is loaded from a named module, not from the classpath, so ALL_UNNAMED may not be sufficient. One workaround used in similar scenarios is to add the Elytron module explicitly in the --add-opens target, for example:--add-opens java.base/sun.security.x509=org.wildfly.security.elytron
or, more generally, to the WildFly module that contains the classes doing the reflective call. Alternatively, some users have moved the --add-opens settings from standalone.conf into the JAVA_TOOL_OPTIONS environment variable so that they are picked up earlier in the JVM bootstrap; this can make a difference depending on how the server is launched. Since you are constrained to WildFly 26 for Jakarta EE 8, tightening the --add-opens target to the actual Elytron module is likely the most pragmatic workaround.
If you can share (even in redacted form) the current application-security-domain / http-authentication-factory configuration that wires datawaveElytron, I can try to sketch a concrete configuration that both ensures DATAWAVE-AUTH is always selected and keeps the client certificate available to your realm.
Best regards,
Luca
To view this discussion visit https://groups.google.com/d/msgid/wildfly/f71fd8bc-fe52-4827-a0f5-23f0ab51c91en%40googlegroups.com.
Hi Luca,
Thank you for the additional feedback, I appreciate the suggestions you have made.
IllegalAccessException
I will attempt adding the --add-opens target to the Elytron module as a workaround for the IllegalAccessException.
SSL Cert Chain Suggestions
For your two suggestions on how obtain the certificate chain to pass into the custom realm:
expose the X509Certificate (or chain) as a credential in your custom realm (for example by writing a small Elytron RealmMapper/SecurityRealm that stores it as an X509CertificateChainCredential), or
keep accessing the certificate at the SSL layer and pass it into your realm as part of the identity creation.
I am not entirely certain how to go about either of these within the context of Wildfly. Currently within the DatawaveHttpAuthenticationMechanism, I am attempting to obtain relevant evidence (in this case, the ProxiedX509CertificateEvidence), and then invoke the security realm verifyEvidence(Evidence evidence) method via a EvidenceVerifyCallback which will then attempt to load an identity.
Snippet from DatawaveHttpAuthenticationMechanism that invokes the callback during a call to evaluateRequest(HttpServerRequest request):
try {
// Obtains evidence from JWT, trusted headers, or proxied SSL.
Evidence evidence = getEvidence(request);
// Invoke call back to verify evidence and load identity in security realm.
EvidenceVerifyCallback callback = new EvidenceVerifyCallback(evidence);
callbackHandler.handle(new Callback[]{callback});
} catch (Exception e) {
log.error("Failed to verify evidence", e);
request.authenticationFailed("Authentication failed");
}
Snippet DatawaveSecurityRealm showing part of the logic for
@Override
public RealmIdentity getRealmIdentity(Evidence evidence) {
if (log.isTraceEnabled()) {
log.trace("getRealmIdentity(), evidence=" + evidence);
}
// Ensure that identity providers have been initialized since the last call to initialize().
initializeProviders();
return new DatawaveRealmIdentity(this, evidence);
}
/**
* A {@link RealmIdentity} implementation that represents a specific authentication attempt against this security realm.
*/
private static class DatawaveRealmIdentity implements RealmIdentity {
private final DatawaveSecurityRealm datawaveSecurityRealm;
/**
* The underlying evidence representing the user to be authenticated.
*/
private final Evidence evidence;
/**
* The identity found that is associated with the evidence.
*/
private EvidenceIdentity identity;
/**
* Whether an attempt has been made to load the identity associated with the evidence.
*/
private boolean loaded = false;
public DatawaveRealmIdentity(DatawaveSecurityRealm datawaveSecurityRealm, Evidence evidence) {
this.datawaveSecurityRealm = datawaveSecurityRealm;
this.evidence = evidence;
}
@Override
public Principal getRealmIdentityPrincipal() {
try {
if (exists()) {
return new DatawavePrincipal(identity.getUsers());
}
} catch (RealmUnavailableException e) {
return null;
}
return null;
}
@Override
public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
Preconditions.checkNotNull(evidence, "Parameter evidence may not be null");
try {
loadIdentity();
} catch (Exception e) {
throw new RealmUnavailableException("Error occurred while verifying evidence", e);
}
return exists();
}
@Override
public boolean exists() throws RealmUnavailableException {
try {
return this.identity != null;
} catch (Exception e) {
throw new RealmUnavailableException("Error occurred when checking if identity exists", e);
}
}
If either of the possible solutions you suggested above would allow me to pass the SSL cert as evidence to the security realm, that is the approach I would like to go with. Otherwise, I request some additional detail for how I should integrate your suggestions.
Requested Configuration Snippets
We primarily configure Wildfly by supplying a configuration.cli file. The following snippet configures the datawaveElytron security domain.
#
# Configure datawave security domain (elytron)
#
# Create a custom role decoder that will decode the roles a user has from a DatawavePrincipal.
/subsystem=elytron/custom-role-decoder=datawaveRoleDecoder:add( \
class-name=datawave.security.realm.RequiredRoleDecoder, \
module="datawave.security", \
configuration={})
# Create a custom realm that handles preparing the DatawavePrincipal for authentication.
/subsystem=elytron/custom-realm=datawaveRealm:add( \
class-name=datawave.security.realm.DatawaveSecurityRealm, \
module="datawave.security", \
configuration={ \
certVerifier="datawave.security.cert.DatawaveCertVerifier", \
ocspLevel="off", \
trustedHeadersEnabled="${dw.trusted.header.authentication:false}" \
})
# Create and configure the security domain 'datawaveElytron' that will handle the authentication and authorization.
/subsystem=elytron/security-domain=datawaveElytron:add(default-realm=datawaveRealm,realms=[{realm=datawaveRealm}],role-decoder=datawaveRoleDecoder)
The following snippet configures the http-authentication-factory.
#
# Configure the authentication mechanism for Elytron.
#
/subsystem=elytron/service-loader-http-server-mechanism-factory=datawaveMechanismFactory:add( \
module=datawave.security)
/subsystem=elytron/http-authentication-factory=datawaveAuthenticationFactory:add( \
http-server-mechanism-factory=datawaveMechanismFactory, \
security-domain=datawaveElytron, \
mechanism-configurations=[{mechanism-name=DATAWAVE-AUTH,mechanism-realm-configurations=[{realm-name=datawave}]}])
I am not sure if the realm-name value above in mechanism-configurations should be datawaveRealm, datawaveElytron, or datawave.
The following configures the application-security-domain 'datawave'.
#
# Ensure undertow is configured to use the custom authentication mechanism.
#
# Configure a security domain for the application that uses the datawave mechanism factory. Override the deployment
# config in order to support the use of DATAWAVE-AUTH instead of BASIC only by default.
/subsystem=undertow/application-security-domain=datawave:add( \
http-authentication-factory=datawaveAuthenticationFactory, \
override-deployment-config=true)
# Ensure the default application security domain is 'datawave'.
/subsystem=undertow:write-attribute( \
name=default-security-domain, value="datawave")
The following makes the datawaveElytron security domain discoverable by EJBs as datawave. I am not sure if this would cause a conflict with the application security domain datawave.
#
# EJB subsystem configuration
#
# Map the 'datawaveElytron' security domain to the name 'datawave' and make it discoverable by EJBs.
/subsystem=ejb3/application-security-domain=datawave:add(security-domain=datawaveElytron)
# Set the security domain 'datawave' as the default.
/subsystem=ejb3:write-attribute(name=default-security-domain, value="datawave")
Please let me know if you need additional info.
Thank you,
Laura
Minor edit - The snippet description for the DatawaveSecurityRealm code is incomplete. It should actually be:
Hi Laura,
thank you for the additional details, the code snippets and the clarification about how DatawaveSecurityRealm and DatawaveRealmIdentity work together. That context makes it much clearer how you are currently using EvidenceVerifyCallback and verifyEvidence(Evidence) inside the realm.
Regarding the certificate chain: the good news is that the pattern you already have (obtaining Evidence in DatawaveHttpAuthenticationMechanism and then driving the realm via EvidenceVerifyCallback) is compatible with both of the approaches I mentioned; it is mainly a question of where you want to convert from Elytron’s evidence types to your own identity model.
If you want to keep “passing the SSL cert as evidence” into the realm, then the second option is effectively what you are already doing: at the mechanism level you obtain ProxiedX509CertificateEvidence (or whatever evidence represents the client cert chain after TLS termination) and hand it directly to the realm via EvidenceVerifyCallback. In that case, the natural place to integrate the SSL chain is inside DatawaveRealmIdentity.loadIdentity(): when you first see that the Evidence is a ProxiedX509CertificateEvidence (or wraps such an evidence), you can extract the X509Certificate[] chain and feed it into your existing DatawaveCertVerifier / identity providers so that your EvidenceIdentity contains everything you need.
The first option—exposing the chain as a credential—is more useful if you want the certificate to be queryable after the realm has already established an identity (for example via RealmIdentity.getCredential(Class<?>)). To do that, you could extend DatawaveRealmIdentity so that, once loadIdentity() has run and you have both the user information and the certificate chain, you also implement getCredential(Class<?>) and return an X509CertificateChainCredential when requested. This does not change how you authenticate (you still rely on verifyEvidence(Evidence)), but it gives later Elytron components a standard way to retrieve the chain.
So, to answer your question directly: yes, both solutions can be adapted so that the SSL certificate chain is effectively passed into the realm as part of the evidence‑driven authentication flow. If your main concern is to keep all of the logic in your own realm and avoid additional Elytron plumbing, I would start by enhancing DatawaveRealmIdentity.loadIdentity() to unwrap ProxiedX509CertificateEvidence, extract the chain and feed it to DatawaveCertVerifier, and only add the X509CertificateChainCredential implementation if you later discover a concrete need for other components to read the chain as a credential.
On the configuration side, the CLI snippets you shared for datawaveElytron and datawaveAuthenticationFactory look consistent with using DATAWAVE-AUTH as the sole mechanism for that factory. The remaining piece is how this http-authentication-factory is bound to your application via an application-security-domain and the Undertow application mapping; as long as that wiring is in place, your current pattern of using EvidenceVerifyCallback should continue to work once you add the certificate handling into DatawaveRealmIdentity.
Please let me know how the --add-opens change to the Elytron module works out, and feel free to share a (possibly redacted) version of loadIdentity() if you would like more concrete suggestions on integrating the certificate chain there.
Best regards,
Luca
To view this discussion visit https://groups.google.com/d/msgid/wildfly/b46dccd1-8857-47ab-b34a-b0fa1807eec1n%40googlegroups.com.
Hi Luca,
Thank you for the additional details on the compatibility of the approaches you suggested. I will let you know how the --add-opens change works out once I have a chance to test it. I have a couple of follow-up questions:
Question 1:
After TLS termination, what is the best way to obtain the client cert chain inside a call to DatawaveHttpAuthenticationMechanism.evaluateRequest(HttpServerRequest request)? Should I expect to be able to obtain it via HttpServerRequest.getPeerCertificates()?
Question 2:
For my configuration of the http-authentication-factory, can you confirm if the value of the realm-name property below should be:
datawaveRealm: the custom-realm.
datawaveElytron: the security-domain that references datawaveRealm.
datawave: the application security domain
/subsystem=elytron/http-authentication-factory=datawaveAuthenticationFactory:add( \
http-server-mechanism-factory=datawaveMechanismFactory, \
security-domain=datawaveElytron, \
mechanism-configurations=[{mechanism-name=DATAWAVE-AUTH,mechanism-realm-configurations=[{realm-name=datawave}]}])
Thank you,
Laura
Hi Laura,
great, thank you for the follow‑up questions and for confirming you will test the --add-opens change.
For Question 1: after TLS termination, yes, the usual and preferred way to obtain the client certificate chain inside DatawaveHttpAuthenticationMechanism.evaluateRequest(HttpServerRequest request) is via request.getPeerCertificates(). If mutual TLS is correctly configured and the connection is using client‑auth, Elytron’s HTTP layer will typically expose the chain there as an array of X509Certificate, and you can then wrap it into the appropriate Evidence type (for example ProxiedX509CertificateEvidence) before passing it to your realm via EvidenceVerifyCallback. If getPeerCertificates() returns null or an empty array, that usually indicates that either client certificates were not requested/required on the TLS side or the connection is not the client‑auth one you expect, so that is a good first thing to check when debugging.
For Question 2: in the http-authentication-factory snippet you posted, the security-domain attribute is already pointing at datawaveElytron, which is the Elytron security domain that references datawaveRealm, and that is exactly what you want. The realm-name in mechanism-realm-configurations should match the realm that is visible inside that security domain for the purposes of the HTTP authentication mechanism, so using datawave (the application security domain name) there is correct in this context and aligns with your current configuration. In other words: keep security-domain=datawaveElytron and realm-name=datawave as you have them; you do not need to switch realm-name to datawaveRealm or datawaveElytron.
Please let me know what you see from request.getPeerCertificates() once you have mutual TLS fully wired on WildFly 26, and we can adjust the evidence handling if needed.
Best regards,
Luca
To view this discussion visit https://groups.google.com/d/msgid/wildfly/b4a89e12-5481-4660-9c97-16ea5bef9b6dn%40googlegroups.com.
Hi Luca,
Unfortunately, despite using --add-opens=java.base/sun.security.x509=org.wildfly.security.elytron in the standalone.conf, the IllegalAccessException persisted.
The good news is that I no longer need to address this because I was able to fix my issues with getting the TLS termination to redirect to my custom http authentication mechanism, even when the https-listener is configured to use my server-ssl-context. I removed the reference to the datawaveElytron security domain from my server-ssl-context definition entirely. What was:
/subsystem=elytron/server-ssl-context=serverSSLContext:add( \
key-manager=serverKeyManager, \
trust-manager=serverTrustManager, \
protocols=["TLSv1.1","TLSv1.2"], \
want-client-auth=true, \
security-domain=datawaveElytron)
Is now:
/subsystem=elytron/server-ssl-context=serverSSLContext:add( \
key-manager=serverKeyManager, \
trust-manager=serverTrustManager, \
protocols=["TLSv1.1","TLSv1.2"], \
want-client-auth=true)
The TLS termination now redirects properly and the peer certificate chain can be fetched via HttpServerRequest.getPeerCertificateChain(). As a result of this, the X550Name.asX5900Principal() method is not invoked, and the IllegalAccessException never occurs.
I am now facing a new challenge. In our custom security realm, we need to be able to inject a DatawaveUserService which acts as a store of known users. Previously in Wildfly 17, we were able to inject these in our legacy custom LoginModule through the use of a class that extended javax.enterprise.spi.inject.Extension, and exposed Spring Beans as CDI beans in its afterBeanDiscovery(AfterBeanDiscovery discovery, BeanManager manager) implementation, and allowed us to manually inject the user service into the LoginModule after initialization.
This no longer works in Wildfly 26, I suspect due to the requirement for the custom Elytron classes to be deployed as a separate module rather than bundled with our application.ear. What is the recommended way to handle the need for using CDI with custom security realms? JNDI?
Thank you,
Laura
Hi Laura,
You are right that moving the Elytron custom realm into a separate module changes how CDI and Spring beans can be injected compared to the old, application‑bundled LoginModule approach in WildFly 17. In WildFly 26 there are essentially three practical options to access services such as DatawaveUserService from a custom Elytron realm:
Expose the service as a JNDI‑lookupable resource (for example via a CDI/EJB facade inside your EAR) and have the Elytron module perform a JNDI lookup once at initialization time, caching the reference inside the realm. This is usually the most straightforward option when the realm lives in a separate module.
Wrap the user store access behind a small EJB or CDI bean that is deployed with the application, and call it remotely from the realm via JNDI or a well‑defined interface, keeping the realm itself as thin as possible and focused only on authentication logic.
If you are comfortable keeping a light integration layer inside the application, you can also let the realm work only with simple value‑objects and delegate all Spring/CDI‑specific logic to a component inside the EAR, again reached through JNDI.
In practice, using JNDI is the recommended and supported way to bridge from Elytron custom realms to application‑level services, because it avoids tight coupling to the CDI container and works reliably when the realm is packaged as a global module instead of inside the deployment. I would therefore suggest registering a JNDI‑visible facade for DatawaveUserService in your EAR, and then performing the lookup from DatawaveSecurityRealm during initialization so that the realm can keep using it as before without depending directly on CDI/Spring injection.
Please let me know how that works out, or if you want an example of the JNDI lookup pattern inside a custom realm.
Best regards,
Luca
To view this discussion visit https://groups.google.com/d/msgid/wildfly/c4694cf1-7b88-4bc3-88c0-36c5e94b96c0n%40googlegroups.com.
Hi Luca,
I’ve been running into some challenges trying to get JNDI to work. I created the following EJB provider class:
@Singleton
public class SecurityRealmEJBProvider {
public static SecurityRealmEJBProvider fetchFromJndi() {
log.trace("fetchFromJndi()");
try {
String ejbJndi = System.getProperty("dw.security.realm.ejb.provider.jndi");
if (log.isTraceEnabled()) {
log.trace("Attempting lookup of security realm EJB provider from JNDI: " + ejbJndi);
}
InitialContext context = new InitialContext();
return (SecurityRealmEJBProvider) context.lookup(ejbJndi);
} catch (NamingException e) {
log.error("Failed to look up EJBs from JNDI");
throw new RuntimeException(e);
}
}
private static final Logger log = Logger.getLogger(SecurityRealmEJBProvider.class);
private SSLContextInfo sslContextInfo;
private DatawaveUserService datawaveUserService;
@Inject
public void setSslContextInfo(SSLContextInfo sslContextInfo) {
log.trace("setSSLContextInfo()");
this.sslContextInfo = sslContextInfo;
}
public SSLContextInfo getSslContextInfo() {
return sslContextInfo;
}
@Inject
public void setDatawaveUserService(DatawaveUserService datawaveUserService) {
log.trace("setDatawaveUserService()");
this.datawaveUserService = datawaveUserService;
}
public DatawaveUserService getDatawaveUserService() {
return datawaveUserService;
}
@PostConstruct
public void logStatus() {
log.debug("DatawaveUserService available: " + (datawaveUserService != null));
log.debug("SSLContextInfo available: " + (sslContextInfo != null));
}
}
Where the value of the system property is the JNDI mapping java:global/datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose/gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT/SecurityRealmEJBProvider. The logs confirmed that the EJB instance was bound to JNDI within Wildfly:
2026-02-23 09:56:10,359 INFO [org.jboss.as.ejb3.deployment] (MSC service thread 1-4) WFLYEJB0473: JNDI bindings for session bean named 'SecurityRealmEJBProvider' in deployment unit 'subdeployment "gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT.jar" of deployment "datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose.ear"' are as follows:
java:global/datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose/gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT/SecurityRealmEJBProvider!datawave.security.realm.SecurityRealmEJBProvider
java:app/gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT/SecurityRealmEJBProvider!datawave.security.realm.SecurityRealmEJBProvider
java:module/SecurityRealmEJBProvider!datawave.security.realm.SecurityRealmEJBProvider
java:global/datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose/gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT/SecurityRealmEJBProvider
java:app/gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT/SecurityRealmEJBProvider
java:module/SecurityRealmEJBProvider
Within the DatawaveSecurityRealm class, I attempt to load up the EJB from JNDI during an authentication request:
/**
* Attempt to manually inject the CDI beans for this realm. If any required CDI beans are still null afterward, an {@link IllegalStateException} will be
* thrown.
*/
private void injectBeans() {
log.trace("injectBeans()");
if (!beansInjected) {
if (userService == null || sslContextInfo == null) {
if (log.isTraceEnabled()) {
log.trace(DatawaveUserService.class.getName() + " requires injection: " + (userService == null));
log.trace(SSLContextInfo.class.getName() + " requires injection: " + (sslContextInfo == null));
}
SecurityRealmEJBProvider ejbProvider = SecurityRealmEJBProvider.fetchFromJndi();
this.userService = ejbProvider.getDatawaveUserService();
this.sslContextInfo = ejbProvider.getSslContextInfo();
}
// Verify that the user service is not null after injection.
if (userService == null) {
throw new IllegalStateException("Failed to inject " + DatawaveUserService.class.getName());
}
// Verify the ssl context is not null after injection.
if (sslContextInfo == null) {
throw new IllegalStateException("Failed to inject " + SSLContextInfo.class.getName());
}
beansInjected = true;
}
}
But I got the following ClassCastException:
Caused by: java.lang.ClassCastException: class datawave.security.realm.SecurityRealmEJBProvider$$$view5 cannot be cast to class datawave.security.realm.SecurityRealmEJBProvider (datawave.security.realm.SecurityRealmEJBProvider$$$view5 is in unnamed module of loader 'deployment.datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose.ear.gov.nsa.datawave.webservices-datawave-ws-security-7.35.0-SNAPSHOT.jar' @614f566f; datawave.security.realm.SecurityRealmEJBProvider is in unnamed module of loader 'datawave.security' @1bede7e)
at datawave.security//datawave.security.realm.SecurityRealmEJBProvider.fetchFromJndi(SecurityRealmEJBProvider.java:26)
at datawave.security//datawave.security.realm.DatawaveSecurityRealm.injectBeans(DatawaveSecurityRealm.java:183)
at datawave.security//datawave.security.realm.DatawaveSecurityRealm.initializeProviders(DatawaveSecurityRealm.java:158)
at datawave.security//datawave.security.realm.DatawaveSecurityRealm.getRealmIdentity(DatawaveSecurityRealm.java:326)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.auth.server.ServerAuthenticationContext$UnassignedState.verifyEvidence(ServerAuthenticationContext.java:1729)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.auth.server.ServerAuthenticationContext.verifyEvidence(ServerAuthenticationContext.java:767)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.auth.server.ServerAuthenticationContext$1.handleOne(ServerAuthenticationContext.java:1021)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.auth.server.ServerAuthenticationContext$1.handle(ServerAuthenticationContext.java:868)
at org.wildfly.secu...@1.19.1.Final//org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$SecurityIdentityCallbackHandler.handle(SecurityIdentityServerMechanismFactory.java:126)
at datawave.security//datawave.security.auth.DatawaveHttpAuthenticationMechanism.handleCallback(DatawaveHttpAuthenticationMechanism.java:231)
There seem to be issues with the classloader. The custom Wildfly module that I deploy with the custom Wildfly components are also bundled with the application EAR that we deploy separately as an EJB module. I thought perhaps this was the issue, so I split the original security Maven project into two Maven projects, one with the EJB definitions (datawave-ws-security), and one with just the custom Elytron components (datawave-ws-security-elytron) which has a dependency on datawave-ws-security. The module.xml for the custom Wildfly module now looks like the following, where dependencies that are also bundled in the ear are highlighted. The jar datawave-ws-security-elytron-7.35.0-SNAPSHOT.jar is not bundled with the application EAR.
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.3" name="datawave.security">
<resources>
<resource-root path="datawave-ws-security-elytron-7.35.0-SNAPSHOT.jar"/>
<resource-root path="datawave-ws-security-7.35.0-SNAPSHOT.jar"/>
<resource-root path="datawave-ws-common-7.35.0-SNAPSHOT.jar"/>
<resource-root path="datawave-ws-common-util-7.35.0-SNAPSHOT.jar"/>
<resource-root path="datawave-core-common-util-7.35.0-SNAPSHOT.jar"/>
<resource-root path="common-utils-7.35.0-SNAPSHOT.jar"/>
<resource-root path="authorization-api-4.0.0.jar"/>
<resource-root path="jackson-datatype-guava-2.13.5.jar"/>
<resource-root path="jackson-module-jaxb-annotations-2.13.5.jar"/>
<resource-root path="guava-31.1-jre.jar"/>
<resource-root path="commons-lang3-3.8.1.jar"/>
<resource-root path="slf4j-api-2.0.17.jar"/>
</resources>
<dependencies>
<module name="org.apache.log4j"/>
<module name="org.wildfly.security.elytron"/>
<module name="org.wildfly.extension.elytron"/>
<module name="com.fasterxml.jackson.core.jackson-core"/>
<module name="com.fasterxml.jackson.core.jackson-databind"/>
</dependencies>
</module>
Unfortunately, while this did deploy to Wildfly, I now get the following error:
2026-02-23 11:06:14,320 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 104) MSC000001: Failed to start service jboss.deployment.subunit."datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose.ear"."gov.nsa.datawave.webservices-datawave-ws-query-websocket-7.35.0-SNAPSHOT.war".undertow-deployment: org.jboss.msc.service.StartException in service jboss.deployment.subunit."datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose.ear"."gov.nsa.datawave.webservices-datawave-ws-query-websocket-7.35.0-SNAPSHOT.war".undertow-deployment: java.lang.RuntimeException: java.lang.IllegalStateException: There are no mechanisms available from the HttpAuthenticationFactory.
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:90)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.jbos...@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jbos...@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.base/java.lang.Thread.run(Thread.java:829)
at org.jbos...@2.4.0.Final//org.jboss.threads.JBossThread.run(JBossThread.java:513)
Caused by: java.lang.RuntimeException: java.lang.IllegalStateException: There are no mechanisms available from the HttpAuthenticationFactory.
at io.undert...@2.2.19.Final//io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:257)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:105)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:87)
... 8 more
Caused by: java.lang.IllegalStateException: There are no mechanisms available from the HttpAuthenticationFactory.
at org.wildfly.security.elytron...@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.AuthenticationManager.initialSecurityHandler(AuthenticationManager.java:120)
at org.wildfly.security.elytron...@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.AuthenticationManager.lambda$configure$2(AuthenticationManager.java:101)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.DeploymentManagerImpl.setupSecurityHandlers(DeploymentManagerImpl.java:445)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.DeploymentManagerImpl.access$600(DeploymentManagerImpl.java:122)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:226)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:187)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.ext...@26.1.3.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at io.undert...@2.2.19.Final//io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:255)
... 10 more
If relevant, this is our jboss-deployment-structure.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
<!-- share classpath across all sub-deployments -->
<ear-subdeployments-isolated>false</ear-subdeployments-isolated>
<deployment>
<dependencies>
<module name="datawave.webservice.configuration" export="true" />
<module name="org.apache.hadoop.common" export="true" />
<!-- Fixes Spring context loading issues on Java 11 -->
<module name="jdk.unsupported" export="true" />
</dependencies>
</deployment>
</jboss-deployment-structure>
Is the ClassCastException an issue due to how my custom Wildfly module is structured? Do I need to avoid having dependencies on project jars that are also bundled with the EAR we deploy? Or can the ClassCastException be resolved another way?
Any suggestions would be appreciated. Please let me know if you need additional details.
Thanks,
Laura
I fixed the issue causing the following error with the split Maven modules:
2026-02-23 11:06:14,320 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 104) MSC000001: Failed to start service jboss.deployment.subunit."datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose.ear"."gov.nsa.datawave.webservices-datawave-ws-query-websocket-7.35.0-SNAPSHOT.war".undertow-deployment: org.jboss.msc.service.StartException in service jboss.deployment.subunit."datawave-ws-deploy-application-7.35.0-SNAPSHOT-compose.ear"."gov.nsa.datawave.webservices-datawave-ws-query-websocket-7.35.0-SNAPSHOT.war".undertow-deployment: java.lang.RuntimeException: java.lang.IllegalStateException: There are no mechanisms available from the HttpAuthenticationFactory.
I had mistakenly moved the org.wildfly.http.HttpServerAuthenticationMechanismFactory file directly to security-elytron/src/main/resources rather than security-elytron/src/main/resources/META-INF/services. This error is no longer occurring, but I am now getting the same ClassCastException due to conflicting class versions between the EAR and security module classloaders.
Regards,
Laura