Skip to content

Verify the connector server certificate against the host over the WebSocket connection - #123

Open
vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:websocket-client-hostname-verification
Open

vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:websocket-client-hostname-verification

Conversation

@vharseko

Copy link
Copy Markdown
Member

Companion to #122, which fixes the same class of problem in the legacy connector server client (CodeQL alert #24). This one covers the Grizzly WebSocket client in connector-framework-server, which CodeQL does not model because the SSLEngine is created inside Grizzly.

Problem

ConnectionManager wraps wss:// connections in a Grizzly SSLFilter whose SSLEngine validates the certificate chain but never checks the certificate against the host. Anyone holding a certificate the client trusts could sit in the middle of the connection to the connector server; with the usual setup (server certificate imported into the JVM truststore next to the public CAs) any publicly trusted certificate was enough.

Change

  • ConnectionManager.SwitchingSSLFilter:
    • createClientSSLEngine creates the engine for the host of the remote URI (not for the proxy the socket may be connected to; IPv6 brackets stripped) and enables JSSE "HTTPS" endpoint identification via SSLParameters, so the certificate is verified against that host during the handshake, before any application data is sent. Applies to custom trust managers too, plain X509TrustManager included.
    • notifyHandshakeFailed logs TLS handshake with connector server host:port failed: <cause> at WARN and records the cause on the connection.
  • ClientRemoteConnectorInfoManager: the close listener reports that cause — TLS handshake failed before WebSocket is established: … with the SSLHandshakeException chained — instead of the bare Connection is closed before WebSocket is established.
  • ConnectionManagerConfig: hostnameVerification getter/setter, defaulting to the same system property the legacy client honours in Verify the connector server certificate against the host over the legacy SSL connection #122, org.identityconnectors.framework.remote.hostnameVerification. Only the literal false disables the check.

Tests

ClientHostnameVerificationTest (connector-server-grizzly): a server with a CN-only certificate is rejected when connecting to wss://127.0.0.1 (cause chain carries SSLHandshakeException: No subject alternative names present), a certificate with matching subjectAltName is accepted and the test connector is looked up over it, and the mismatching certificate is accepted when hostnameVerification=false.

The module's test certificates had expired in January 2025 (they only kept working because a self-signed end-entity certificate that is itself a trust anchor skips validity checks). They are regenerated with 30 years of validity; serverKeystore.jks now names localhost, 127.0.0.1 and ::1, which the client verifies, serverKeystore-cn-only.jks keeps a certificate without subjectAltName for the negative test, and README.txt has the keytool commands. AsyncRemoteSecureConnectorInfoManagerTest runs unchanged against the new certificate with verification on.

Local runs: connector-framework-server 29 tests, connector-server-grizzly 37 tests, connector-server-jetty 34 tests, all green.

Compatibility

Deployments whose connector server certificate does not name the host used in the wss:// URI fail the handshake now, with the reason in the log and in the exception. The fix is a certificate that names the host; the config switch / system property is the temporary escape hatch. Worth a release-notes entry together with #122.

Noticed, not changed

…Socket connection

The Grizzly client wraps its wss:// connections in an SSLEngine, which
validates the certificate chain but never checks the certificate against
the host, so anyone holding a certificate the client trusts could sit in
the middle of the connection. Have JSSE do "HTTPS" endpoint identification
during the handshake, against the host of the remote URI also when the
socket goes through a proxy. ConnectionManagerConfig gets a
hostnameVerification switch that defaults to the system property the
legacy client honours, org.identityconnectors.framework.remote.hostnameVerification.

A failed handshake is logged at WARN and surfaces as the cause of the
"before WebSocket is established" failure instead of a bare connection close.

The test certificates expired in January 2025 and are regenerated; the
server certificate now names localhost, 127.0.0.1 and ::1 as the client
verifies them, and serverKeystore-cn-only.jks keeps one without
subjectAltName for the negative test.
@vharseko vharseko added security Security fix / CVE remediation java Pull requests that update java code tests Test additions or fixes framework OpenICF-java-framework labels Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework OpenICF-java-framework java Pull requests that update java code security Security fix / CVE remediation tests Test additions or fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant