From 88ba87a8194268193f56ce6686b59760dc292f06 Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Mon, 27 May 2024 19:20:00 +0200 Subject: [PATCH] Use SSL WebSockets for Nitro to prevent CSP issues --- .../proxy/nitro/NitroProxyProvider.java | 10 ++-- .../http/NitroCertificateSniffingManager.java | 23 +++++++- .../proxy/nitro/http/NitroHttpProxy.java | 53 ++++++++----------- .../nitro/http/NitroSslContextFactory.java | 20 +++++++ .../nitro/websocket/NitroWebsocketProxy.java | 25 +++++++-- 5 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java index b86a4fc..c073a1a 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java @@ -6,6 +6,8 @@ import gearth.protocol.connection.HProxySetter; import gearth.protocol.connection.HState; import gearth.protocol.connection.HStateSetter; import gearth.protocol.connection.proxy.ProxyProvider; +import gearth.protocol.connection.proxy.nitro.http.NitroAuthority; +import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback; import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy; @@ -13,7 +15,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.ServerSocket; import java.util.concurrent.atomic.AtomicBoolean; public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener { @@ -32,11 +33,14 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa private String originalCookies; public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection) { + final NitroAuthority authority = new NitroAuthority(); + final NitroCertificateSniffingManager certificateManager = new NitroCertificateSniffingManager(authority); + this.proxySetter = proxySetter; this.stateSetter = stateSetter; this.connection = connection; - this.nitroHttpProxy = new NitroHttpProxy(this); - this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this); + this.nitroHttpProxy = new NitroHttpProxy(this, certificateManager); + this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this, certificateManager); this.abortLock = new AtomicBoolean(); } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java index 35f4c2d..c5c3d63 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java @@ -20,12 +20,31 @@ public class NitroCertificateSniffingManager implements MitmManager { private static final boolean DEBUG = false; private final BouncyCastleSslEngineSource sslEngineSource; + private final Authority authority; - public NitroCertificateSniffingManager(Authority authority) throws RootCertificateException { + public NitroCertificateSniffingManager(Authority authority) { + this.authority = authority; try { sslEngineSource = new BouncyCastleSslEngineSource(authority, true, true, null); } catch (final Exception e) { - throw new RootCertificateException("Errors during assembling root CA.", e); + throw new RuntimeException(new RootCertificateException("Errors during assembling root CA.", e)); + } + } + + public Authority getAuthority() { + return authority; + } + + public SSLEngine websocketSslEngine(String commonName) { + final SubjectAlternativeNameHolder san = new SubjectAlternativeNameHolder(); + + san.addDomainName("localhost"); + san.addIpAddress("127.0.0.1"); + + try { + return sslEngineSource.createCertForHost(commonName, san); + } catch (Exception e) { + throw new FakeCertificateException("Failed to create WebSocket certificate", e); } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java index c0098f3..cb7e0ba 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java @@ -12,8 +12,6 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; -import org.littleshoot.proxy.mitm.Authority; -import org.littleshoot.proxy.mitm.RootCertificateException; import java.io.File; import java.io.IOException; @@ -25,20 +23,20 @@ public class NitroHttpProxy { private static final String ADMIN_WARNING_KEY = "admin_warning_dialog"; private static final AtomicBoolean SHUTDOWN_HOOK = new AtomicBoolean(); - private final Authority authority; private final NitroOsFunctions osFunctions; private final NitroHttpProxyServerCallback serverCallback; + private final NitroCertificateSniffingManager certificateManager; private HttpProxyServer proxyServer = null; - public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback) { + public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback, NitroCertificateSniffingManager certificateManager) { this.serverCallback = serverCallback; - this.authority = new NitroAuthority(); + this.certificateManager = certificateManager; this.osFunctions = NitroOsFunctionsFactory.create(); } private boolean initializeCertificate() { - final File certificate = this.authority.aliasFile(".pem"); + final File certificate = this.certificateManager.getAuthority().aliasFile(".pem"); // All good if certificate is already trusted. if (this.osFunctions.isRootCertificateTrusted(certificate)) { @@ -80,7 +78,7 @@ public class NitroHttpProxy { return false; } - return this.osFunctions.installRootCertificate(this.authority.aliasFile(".pem")); + return this.osFunctions.installRootCertificate(this.certificateManager.getAuthority().aliasFile(".pem")); } /** @@ -100,33 +98,28 @@ public class NitroHttpProxy { public boolean start() { setupShutdownHook(); - try { - proxyServer = DefaultHttpProxyServer.bootstrap() - .withPort(NitroConstants.HTTP_PORT) - .withManInTheMiddle(new NitroCertificateSniffingManager(authority)) - .withFiltersSource(new NitroHttpProxyFilterSource(serverCallback)) - .withTransparent(true) - .start(); + proxyServer = DefaultHttpProxyServer.bootstrap() + .withPort(NitroConstants.HTTP_PORT) + .withManInTheMiddle(this.certificateManager) + .withFiltersSource(new NitroHttpProxyFilterSource(serverCallback)) + .withTransparent(true) + .start(); - if (!initializeCertificate()) { - proxyServer.stop(); + if (!initializeCertificate()) { + proxyServer.stop(); - System.out.println("Failed to initialize certificate"); - return false; - } - - if (!registerProxy()) { - proxyServer.stop(); - - System.out.println("Failed to register certificate"); - return false; - } - - return true; - } catch (RootCertificateException e) { - e.printStackTrace(); + System.out.println("Failed to initialize certificate"); return false; } + + if (!registerProxy()) { + proxyServer.stop(); + + System.out.println("Failed to register certificate"); + return false; + } + + return true; } public void pause() { diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java new file mode 100644 index 0000000..9e12b69 --- /dev/null +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java @@ -0,0 +1,20 @@ +package gearth.protocol.connection.proxy.nitro.http; + +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import javax.net.ssl.SSLEngine; + +public class NitroSslContextFactory extends SslContextFactory.Server { + + private final NitroCertificateSniffingManager certificateManager; + + public NitroSslContextFactory(NitroCertificateSniffingManager certificateManager) { + this.certificateManager = certificateManager; + } + + @Override + public SSLEngine newSSLEngine(String host, int port) { + System.out.printf("[NitroSslContextFactory] Creating SSLEngine for %s:%d%n", host, port); + return certificateManager.websocketSslEngine(host); + } +} diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java index c60f8c2..a21244c 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java @@ -4,7 +4,8 @@ import gearth.protocol.HConnection; import gearth.protocol.connection.HProxySetter; import gearth.protocol.connection.HStateSetter; import gearth.protocol.connection.proxy.nitro.NitroProxyProvider; -import org.eclipse.jetty.server.Connector; +import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager; +import gearth.protocol.connection.proxy.nitro.http.NitroSslContextFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -21,19 +22,37 @@ public class NitroWebsocketProxy { private final HStateSetter stateSetter; private final HConnection connection; private final NitroProxyProvider proxyProvider; + private final NitroCertificateSniffingManager certificateManager; private final Server server; + private final int serverPort; - public NitroWebsocketProxy(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) { + public NitroWebsocketProxy(HProxySetter proxySetter, + HStateSetter stateSetter, + HConnection connection, + NitroProxyProvider proxyProvider, + NitroCertificateSniffingManager certificateManager) { this.proxySetter = proxySetter; this.stateSetter = stateSetter; this.connection = connection; this.proxyProvider = proxyProvider; - this.server = new Server(0); + this.certificateManager = certificateManager; + this.server = new Server(); + this.serverPort = 0; } public boolean start() { try { + // Configure SSL. + final NitroSslContextFactory sslContextFactory = new NitroSslContextFactory(this.certificateManager); + final ServerConnector sslConnector = new ServerConnector(server, sslContextFactory); + + sslConnector.setPort(this.serverPort); + + // Add SSL to the server. + server.addConnector(sslConnector); + + // Configure the WebSocket. final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/");