From a59c22ca29ead5e4cdbedec122643dea3651ed6c Mon Sep 17 00:00:00 2001 From: Nagaraju Kotcharlakota Date: Thu, 23 Apr 2026 14:53:07 -0700 Subject: [PATCH] Fix TLS certificate validation in Java SDK WebSocket relay connections For Sev2.5 31000000590337 The Java SDK's WebSocketConnector used InsecureTrustManagerFactory.INSTANCE for all wss:// relay connections, which bypassed TLS certificate validation entirely. This allowed a man-in-the-middle attacker to intercept the WebSocket handshake, capture the Authorization: tunnel header, and replay the token against a live tunnel to impersonate the client. What changed - WebSocketConnector.java: The SSL context now uses the JDK's default trust manager for production relay connections, which properly validates server certificates against the system's trusted CA store. - Localhost exceptions are preserved for local development (localhost and tunnels.local.api.visualstudio.com), matching the pattern used by the Go SDK. --- .../tunnels/websocket/WebSocketConnector.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/java/src/main/java/com/microsoft/tunnels/websocket/WebSocketConnector.java b/java/src/main/java/com/microsoft/tunnels/websocket/WebSocketConnector.java index 85ddfc88..9f459912 100644 --- a/java/src/main/java/com/microsoft/tunnels/websocket/WebSocketConnector.java +++ b/java/src/main/java/com/microsoft/tunnels/websocket/WebSocketConnector.java @@ -19,6 +19,9 @@ import java.net.SocketAddress; import java.net.URI; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; + import org.apache.sshd.common.AttributeRepository; import org.apache.sshd.common.io.IoConnectFuture; import org.apache.sshd.common.io.IoHandler; @@ -69,9 +72,27 @@ protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (factory.webSocketUri.getScheme().equals("wss")) { - SslContext sslContext = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - p.addLast("ssl", new SslHandler(sslContext.newEngine(ch.alloc()))); + String host = factory.webSocketUri.getHost(); + boolean isLocalDev = "localhost".equals(host) + || "tunnels.local.api.visualstudio.com".equals(host); + + SslContextBuilder builder = SslContextBuilder.forClient(); + if (isLocalDev) { + builder.trustManager(InsecureTrustManagerFactory.INSTANCE); + } + SslContext sslContext = builder.build(); + + var relayPort = factory.webSocketUri.getPort(); + if (relayPort == -1) { + relayPort = 443; + } + SSLEngine engine = sslContext.newEngine(ch.alloc(), host, relayPort); + if (!isLocalDev) { + SSLParameters params = engine.getSSLParameters(); + params.setEndpointIdentificationAlgorithm("HTTPS"); + engine.setSSLParameters(params); + } + p.addLast("ssl", new SslHandler(engine)); } p.addLast(new HttpClientCodec()); p.addLast(new HttpObjectAggregator(8192));