Search code examples
javasslwebsocketjava-websocket

Java Secure Websocket - Load SSL context from TLS cert file and connect to WSS URI


I'm looking for a way to load a TLS cert file (ssl_cert_auth_params) that looks like this:

-----BEGIN CERTIFICATE-----
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
************************
-----END CERTIFICATE-----

In a Java WSS (Secure WebSocket) client.

I can easily setup the ssl_context in python with this:

import ssl
import pathlib

ssl_context   = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
localhost_pem = pathlib.Path(__file__).resolve().parent / "../../folder/ssl_cert_auth_params"
ssl_context.load_verify_locations(localhost_pem)

And connect to WSS uri with this:

import asyncio
import websockets

async def connect_to_uri(uri, ssl_context):
    ws = await websockets.connect(uri, ssl=ssl_context, ping_interval=3)
    return (ws)

loop = asyncio.get_event_loop()
ws = loop.run_until_complete(connect_to_uri(uri, ssl_context))

but I cannot find an easy way to do the same with a java wss client library.


Solution

  • I am not a websocket expert but here is a snippet which should do the trick for you in java with WebSockets for Jetty:

    I used the following libraries:

    <dependency>
       <groupId>org.eclipse.jetty.websocket</groupId>
       <artifactId>websocket-client</artifactId>
       <version>9.4.41.v20210516</version>
    </dependency>
    <dependency>
       <groupId>io.github.hakky54</groupId>
       <artifactId>sslcontext-kickstart-for-jetty</artifactId>
       <version>6.6.1</version>
    </dependency>
    

    And the Java code:

    import nl.altindag.ssl.SSLFactory;
    import nl.altindag.ssl.util.CertificateUtils;
    import nl.altindag.ssl.util.JettySslUtils;
    import org.eclipse.jetty.client.HttpClient;
    import org.eclipse.jetty.websocket.api.Session;
    import org.eclipse.jetty.websocket.api.WebSocketListener;
    import org.eclipse.jetty.websocket.client.WebSocketClient;
    
    import java.net.URI;
    import java.nio.file.Paths;
    
    public class WebSocketExample {
    
        public static void main(String[] args) throws Exception {
            var sslFactory = SSLFactory.builder()
                    .withTrustMaterial(CertificateUtils.loadCertificate(Paths.get("/path/to/certificate.pem")))
                    .build();
    
            var sslContextFactory = JettySslUtils.forClient(sslFactory);
            var httpClient = new HttpClient(sslContextFactory);
            var webSocketClient = new WebSocketClient(httpClient);
            webSocketClient.start();
    
            var session = webSocketClient.connect(new MyWebSocketListener(), new URI("ws://echo.websocket.org")).get();
            session.getRemote().sendString("Hello there!");
        }
    
        private static class MyWebSocketListener implements WebSocketListener {
    
            @Override
            public void onWebSocketBinary(byte[] bytes, int i, int i1) {
                System.out.println();
            }
    
            @Override
            public void onWebSocketText(String response) {
                System.out.println(response);
            }
    
            @Override
            public void onWebSocketClose(int i, String s) {
                System.out.println("closed");
            }
    
            @Override
            public void onWebSocketConnect(Session session) {
                System.out.println("connected");
            }
    
            @Override
            public void onWebSocketError(Throwable throwable) {
                System.out.println("got error");
            }
        }
    
    }
    

    Jetty SslContextFactory does not have a function which will directly read and use the pem files. So you would end up adding logic to read the pem file and map it into a KeyStore object and then supply it to a TrustManager and then create a SSLContext from it. Therefore I suggest my own library to hide away the verbosity. I hope you like it :)