Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connecting to a secured websocket

Tags:

java

jetty

tyrus

I'm trying to connect to a secured websocket using Jetty (or any other library).

The issue is I get a "No trusted certificate found" error. I'm using a self-signed certificate generated with keytool. What could be done?

import java.net.URI;
import java.util.concurrent.Future;

import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.WebSocketClient;

public class Socket extends WebSocketAdapter{

    public static void main(String[] args) {
        String url = "wss://qa.sockets.stackexchange.com/"; //or "wss://echo.websocket.org"
        // making sure the the jvm find keystore
        String JAVASEC="C:/Program Files/Java/jdk1.8.0_25/jre/lib/security/";
        System.setProperty("javax.net.ssl.keyStore", JAVASEC+"keystore.jks");
        System.setProperty("javax.net.ssl.trustStore", JAVASEC+"cacerts.jks");
        System.setProperty("javax.net.ssl.keyStorePassword", "changeit");

        System.out.println(System.getProperty("javax.net.ssl.trustStore"));
        SslContextFactory sslContextFactory = new SslContextFactory();
        Resource keyStoreResource = Resource.newResource(Socket.class.getResource("/keystore.jks"));//generated with keytool
        sslContextFactory.setKeyStoreResource(keyStoreResource);
        sslContextFactory.setKeyStorePassword("password");
        sslContextFactory.setKeyManagerPassword("password");
        WebSocketClient client = new WebSocketClient(sslContextFactory);
        try{
            client.start();
            Socket socket = new Socket();
            Future<Session> fut = client.connect(socket,URI.create(url));
            Session session = fut.get();
            session.getRemote().sendString("Hello");
        }
        catch (Throwable t){
            t.printStackTrace(System.err);
        }
    }


    @Override
    public void onWebSocketConnect(Session sess){
        super.onWebSocketConnect(sess);
        System.out.println("Socket Connected: " + sess);
    }

    @Override
    public void onWebSocketText(String message){
        super.onWebSocketText(message);
        System.out.println("Received TEXT message: " + message);
    }

    @Override
    public void onWebSocketClose(int statusCode, String reason){
        super.onWebSocketClose(statusCode,reason);
        System.out.println("Socket Closed: [" + statusCode + "] " + reason);
    }

    @Override
    public void onWebSocketError(Throwable cause){
        super.onWebSocketError(cause);
        cause.printStackTrace(System.err);
    }
}

Here's an attempt with Tyrus websocket client, I don't get SSL error, but it prints nothing:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;

import javax.websocket.ClientEndpointConfig;
import javax.websocket.CloseReason;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;

import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.container.grizzly.GrizzlyEngine;

public class ClientWebSocketEndpoint extends Endpoint {

    public static void main(String[] a) throws IOException{

        ClientManager client = ClientManager.createClient();

        //System.getProperties().put("javax.net.debug", "all");
        final SSLContextConfigurator defaultConfig = new SSLContextConfigurator();

        defaultConfig.retrieve(System.getProperties());
            // or setup SSLContextConfigurator using its API.

        SSLEngineConfigurator sslEngineConfigurator =
            new SSLEngineConfigurator(defaultConfig, true, false, false);
        client.getProperties().put(GrizzlyEngine.SSL_ENGINE_CONFIGURATOR,
            sslEngineConfigurator);
        Session session = null;
        final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();

        try {
            session = client.connectToServer(ClientWebSocketEndpoint.class, cec, new URI("wss://qa.sockets.stackexchange.com/"));// or "wss://echo.websocket.org"

        } catch (DeploymentException | URISyntaxException e) {
            e.printStackTrace();
        } finally {
            if (session != null && session.isOpen())
                session.close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, "Bye"));

        }

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        br.readLine();
    }

    @Override
    public void onOpen(Session session, EndpointConfig config) {
        session.addMessageHandler(new MessageHandler.Whole<String>() {

            @Override
            public void onMessage(String message) {
                System.out.println("Received message: "+message);
            }
        });
        try {
            session.getBasicRemote().sendText("1-questions-active");
            session.getBasicRemote().sendText("155-questions-active");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

In comparison this simple code in JS/node works

var WebSocket = require('ws')
  , ws = new WebSocket('wss://qa.sockets.stackexchange.com/');//"wss://echo.websocket.org"

ws.on('message', function(message) {
    console.log('received: %s', message);
});
ws.on('open', function() {
    ws.send('155-questions-active');
    ws.send('1-questions-active');
});

I'd be happy to know a working websocket client in Java

like image 264
caub Avatar asked Mar 21 '15 23:03

caub


2 Answers

The correct way would be to configure your Client keystore / truststore to trust your specific self signed server certificate. There's plenty of answers on stackoverflow demonstrating how to do this with Java by simply configuring the keystore and/or truststore file contents appropriately. (no code necessary).

If you want to "just connect", and you don't care about trust, certificate validity, or even endpoint identification validation (basically all of the good things that make up a well secured connection) then you don't need to mess with keystores, and truststores, or custom X509TrustManagers or even customized SSLContexts to connect to the service. This need is common during development/testing/qa time, but should not be used in production code.

How you would do this with Jetty 9.4.35.v20201120 is to just configure the SslContextFactory.Client (that belongs to the HttpClient, which WebSocketClient is using) to trust all certificates.

import java.net.URI;
import java.util.concurrent.Future;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;

@WebSocket
public class SecureClientSocket
{
    private static final Logger LOG = Log.getLogger(SecureClientSocket.class);

    public static void main(String[] args)
    {
        String url = "wss://qa.sockets.stackexchange.com/";

        SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
        // Tell sslContextFactory to trust all server certificates
        // This is suitable for test/qa environments, and internal environments,
        // but IS NOT SUITABLE FOR PRODUCTION.
        // Note: this is not actually necessary for wss://qa.sockets.stackexchange.com/
        sslContextFactory.setTrustAll(true);
        // If you do choose to comment out the above, this option will cause the
        // Java client side SSL/TLS to validate the server certificate name
        // against the URL used to connect to the server, if it doesn't match
        // then the connection is not established.
        sslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");

        HttpClient httpClient = new HttpClient(sslContextFactory);
        try
        {
            httpClient.start();
            WebSocketClient client = new WebSocketClient(httpClient);
            client.start();
            SecureClientSocket socket = new SecureClientSocket();
            Future<Session> fut = client.connect(socket, URI.create(url));
            Session session = fut.get();
            session.getRemote().sendString("Hello");
            session.getRemote().sendString("155-questions-active");
        }
        catch (Throwable t)
        {
            LOG.warn(t);
        }
    }

    @OnWebSocketConnect
    public void onConnect(Session sess)
    {
        LOG.info("onConnect({})", sess);
    }

    @OnWebSocketClose
    public void onClose(int statusCode, String reason)
    {
        LOG.info("onClose({}, {})", statusCode, reason);
    }

    @OnWebSocketError
    public void onError(Throwable cause)
    {
        LOG.warn(cause);
    }

    @OnWebSocketMessage
    public void onMessage(String msg)
    {
        LOG.info("onMessage() - {}", msg);
    }
}

Which will have results like this ...

2020-12-21 14:03:28.228:INFO::main: Logging initialized @173ms to org.eclipse.jetty.util.log.StdErrLog
2020-12-21 14:03:28.401:WARN:oejusS.config:main: Trusting all certificates configured for Client@4b5a5ed1[provider=null,keyStore=null,trustStore=null]
2020-12-21 14:03:28.402:WARN:oejusS.config:main: No Client EndPointIdentificationAlgorithm configured for Client@4b5a5ed1[provider=null,keyStore=null,trustStore=null]
2020-12-21 14:03:28.862:INFO:j.SecureClientSocket:HttpClient@3712b94-60: onConnect(WebSocketSession[websocket=JettyAnnotatedEventDriver[jetty.SecureClientSocket@3f38289c],behavior=CLIENT,connection=WebSocketClientConnection@7556df66::DecryptedEndPoint@11c4a450{l=/192.168.1.217:46512,r=qa.sockets.stackexchange.com/198.252.206.25:443,OPEN,fill=-,flush=-,to=66/300000},remote=WebSocketRemoteEndpoint@278a12bf[batching=true],incoming=JettyAnnotatedEventDriver[jetty.SecureClientSocket@3f38289c],outgoing=ExtensionStack[queueSize=0,extensions=[],incoming=org.eclipse.jetty.websocket.common.WebSocketSession,outgoing=org.eclipse.jetty.websocket.client.io.WebSocketClientConnection]])
2020-12-21 14:03:30.648:INFO:j.SecureClientSocket:HttpClient@3712b94-62: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"graphicdesign.stackexchange.com\",\"id\":138914,\"titleEncodedFancy\":\"How to achieve purple, paper background texture look?\",\"bodySummary\":\"How is the below purple background texture look achieved? I'm assuming it is a paper texture effect with different layers of light? However when I try to apply both the effect it never turns out the ...\",\"tags\":[\"texture\"],\"lastActivityDate\":1608581010,\"url\":\"https://graphicdesign.stackexchange.com/questions/138914/how-to-achieve-purple-paper-background-texture-look\",\"ownerUrl\":\"https://graphicdesign.stackexchange.com/users/155152/homan-cheung\",\"ownerDisplayName\":\"Homan Cheung\",\"apiSiteParameter\":\"graphicdesign\"}"}
2020-12-21 14:03:30.791:INFO:j.SecureClientSocket:HttpClient@3712b94-60: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"unix.stackexchange.com\",\"id\":457386,\"titleEncodedFancy\":\"IPtables logging is not working in CentOS 7\",\"bodySummary\":\"I want to log all the traffic which comes in and out from the port X. I have followed below steps.\\r\\nEdited /etc/syslog.conf , /etc/rsyslog.conf , /etc/systemd/system/rsyslog.service.d/rsyslog.conf ...\",\"tags\":[\"centos\",\"kernel\",\"iptables\",\"logs\"],\"lastActivityDate\":1608581010,\"url\":\"https://unix.stackexchange.com/questions/457386/iptables-logging-is-not-working-in-centos-7\",\"ownerUrl\":\"https://unix.stackexchange.com/users/301499/karthikeyan-s\",\"ownerDisplayName\":\"Karthikeyan s\",\"apiSiteParameter\":\"unix\"}"}
2020-12-21 14:03:32.235:INFO:j.SecureClientSocket:HttpClient@3712b94-62: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"math.stackexchange.com\",\"id\":812563,\"titleEncodedFancy\":\"Jacobian of exponential mapping in SO3/SE3\",\"bodySummary\":\"Following this post\\nJacobian matrix of the Rodrigues&#39; formula (exponential map)\\n\\nWhat if I really need the Jacobian of the exponential mapping function in $\\\\omega \\\\neq 0$?\\n\\nBasically, I want to ...\",\"tags\":[\"lie-groups\",\"3d\",\"rotations\",\"numerical-optimization\",\"rigid-transformation\"],\"lastActivityDate\":1608581012,\"url\":\"https://math.stackexchange.com/questions/812563/jacobian-of-exponential-mapping-in-so3-se3\",\"ownerUrl\":\"https://math.stackexchange.com/users/153816/user153816\",\"ownerDisplayName\":\"user153816\",\"apiSiteParameter\":\"math\"}"}
2020-12-21 14:03:35.343:INFO:j.SecureClientSocket:HttpClient@3712b94-60: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"raspberrypi.stackexchange.com\",\"id\":76325,\"titleEncodedFancy\":\"GCC version for compiling a loadable kernel module\",\"bodySummary\":\"I am not very familiar with Linux, so may be my question is a little bit foolish.\\n\\nI would like to compile kernel module without recompiling the kernel (USB Wi-Fi stick driver for MT7106U chip). I ...\",\"tags\":[\"kernel\",\"modules\",\"gcc\"],\"lastActivityDate\":1608581015,\"url\":\"https://raspberrypi.stackexchange.com/questions/76325/gcc-version-for-compiling-a-loadable-kernel-module\",\"ownerUrl\":\"https://raspberrypi.stackexchange.com/users/77643/cyclone125\",\"ownerDisplayName\":\"cyclone125\",\"apiSiteParameter\":\"raspberrypi\"}"}
2020-12-21 14:03:36.775:INFO:j.SecureClientSocket:JettyShutdownThread: onClose(1006, Disconnected)
like image 127
Joakim Erdfelt Avatar answered Oct 01 '22 23:10

Joakim Erdfelt


Well, I tried using your code to replicate the problem with no avail ( get the certificate, import it with keytool command and then run the code ). my output is like this.

2015-03-22 23:03:16.192:INFO::main: Logging initialized @503ms
Socket Connected:WebSocketSession[websocket=JettyListenerEventDriver[com.ivan.Main],behavior=CLIENT,connection=WebSocketClientConnection@77ae29b0{IDLE}{f=Flusher[queueSize=0,aggregateSize=0,failure=null],g=Generator[CLIENT,validating],p=Parser@4e086d6d[ExtensionStack,s=START,c=0,len=0,f=null,p=WebSocketPolicy@7c0ceccc[behavior=CLIENT,maxTextMessageSize=65536,maxTextMessageBufferSize=32768,maxBinaryMessageSize=65536,maxBinaryMessageBufferSize=32768,asyncWriteTimeout=60000,idleTimeout=300000,inputBufferSize=4096]]},remote=WebSocketRemoteEndpoint@49f64[batching=true],incoming=JettyListenerEventDriver[com.ivan.Main],outgoing=ExtensionStack[queueSize=0,extensions=[],incoming=org.eclipse.jetty.websocket.common.WebSocketSession,outgoing=org.eclipse.jetty.websocket.client.io.WebSocketClientConnection]]
Received TEXT message: {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"cogsci.stackexchange.com\",\"id\":9404,\"titleEncodedFancy\":\"What&#39;s the difference between repetition suppression and habituation?\",\"bodySummary\":\"Neural repetition suppression seems to be describing behavioral habituation on a neuronal level. What's the difference between these two terms?\\n\",\"tags\":[\"cognitive-neuroscience\",\"terminology\",\"conditioning\",\"perceptual-learning\"],\"lastActivityDate\":1427036607,\"url\":\"http://cogsci.stackexchange.com/questions/9404/whats-the-difference-between-repetition-suppression-and-habituation\",\"ownerUrl\":\"http://cogsci.stackexchange.com/users/7569/recursive-farts\",\"ownerDisplayName\":\"RECURSIVE FARTS\",\"apiSiteParameter\":\"cogsci\"}"}
Received TEXT message: {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"mathematica.stackexchange.com\",\"id\":77933,\"titleEncodedFancy\":\"FunctionDomain doesn&#39;t work with If\",\"bodySummary\":\"When I call FunctionDomain for function defined by formule with If, for example \\n\\nf[x_]:=If[x&lt;0, x, x^3], \\n\\nI get \\\"Unable to find the domain with the available methods\\\". What should I do to get a ...\",\"tags\":[\"functions\"],\"lastActivityDate\":1427036609,\"url\":\"http://mathematica.stackexchange.com/questions/77933/functiondomain-doesnt-work-with-if\",\"ownerUrl\":\"http://mathematica.stackexchange.com/users/27150/adolzi\",\"ownerDisplayName\":\"adolzi\",\"apiSiteParameter\":\"mathematica\"}"}
Received TEXT message: {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"serverfault.com\",\"id\":677390,\"titleEncodedFancy\":\"Smart Array E200i battery on 350ML did not fix the warning message (battery failure)\",\"bodySummary\":\"I have got this warning message on the System Management in a Proliant server 350ML G5.\\n\\nDegraded Accelerator\\nName    Value\\nStatus: Temporarily Disabled\\nError Code: Cache Disabled Low Batteries\\nSerial ...\",\"tags\":[\"hp-proliant\",\"hp-smart-array\"],\"lastActivityDate\":1427036609,\"url\":\"http://serverfault.com/questions/677390/smart-array-e200i-battery-on-350ml-did-not-fix-the-warning-message-battery-fail\",\"ownerUrl\":\"http://serverfault.com/users/164557/dlopezgonzalez\",\"ownerDisplayName\":\"dlopezgonzalez\",\"apiSiteParameter\":\"serverfault\"}"}
Received TEXT message: {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"askubuntu.com\",\"id\":599889,\"titleEncodedFancy\":\"Is downgrading back to 14.04 from 14.10 possible?\",\"bodySummary\":\"I am using Ubuntu 14.10 alongside Windows 7 and I want to degrade to 14.04. I made a bootable pendrive using Universal USB Installer and seems like the pendrive is working fine. On the Installer Boot ...\",\"tags\":[\"14.04\",\"boot\",\"upgrade\",\"downgrade\"],\"lastActivityDate\":1427036610,\"url\":\"http://askubuntu.com/questions/599889/is-downgrading-back-to-14-04-from-14-10-possible\",\"ownerUrl\":\"http://askubuntu.com/users/374332/pkj\",\"ownerDisplayName\":\"pkj\",\"apiSiteParameter\":\"askubuntu\"}"}
Received TEXT message: {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"superuser.com\",\"id\":814765,\"titleEncodedFancy\":\"External Harddrive Shutting Off and On Every So Often While Listening to Music\",\"bodySummary\":\"I am always listening to music on my PC (always via VLC Player) and every so often - maybe once every 2 songs, sometimes more frequently- the music stops playing (VLC's buffer/progress bar stops ...\",\"tags\":[\"hard-drive\",\"power\",\"external\"],\"lastActivityDate\":1427036610,\"url\":\"http://superuser.com/questions/814765/external-harddrive-shutting-off-and-on-every-so-often-while-listening-to-music\",\"ownerUrl\":\"http://superuser.com/users/338547/supercookie47\",\"ownerDisplayName\":\"SuperCookie47\",\"apiSiteParameter\":\"superuser\"}"}
Received TEXT message: {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"math.stackexchange.com\",\"id\":1200778,\"titleEncodedFancy\":\"Continuity of a piecewise function at a specific point\",\"bodySummary\":\"I am having trouble proving the following function is not continuous at $x = 0$ using a formal definition of continuity.\\n\\n$\\nf(x) = \\\\left\\\\{\\n  \\\\begin{array}{lr}\\n    \\\\sin(\\\\frac{1}{x}) &amp; : x \\\\neq 0\\\\\\\\\\n ...\",\"tags\":[\"real-analysis\",\"functions\",\"continuity\",\"epsilon-delta\"],\"lastActivityDate\":1427036612,\"url\":\"http://math.stackexchange.com/questions/1200778/continuity-of-a-piecewise-function-at-a-specific-point\",\"ownerUrl\":\"http://math.stackexchange.com/users/222744/george\",\"ownerDisplayName\":\"George\",\"apiSiteParameter\":\"math\"}"}

So i guess if you want a running web socket client in java, I guess you could just accept all certificate like the link that @tinker has gave to you.

The code should be like this. maybe you could try it and see what happen in your place.

import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.concurrent.Future;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.WebSocketClient;

public class Main extends WebSocketAdapter {

    public static void main(String[] args) {
    String url = "wss://qa.sockets.stackexchange.com/"; // or
                            // "wss://echo.websocket.org"
SslContextFactory sslContextFactory = new SslContextFactory();
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    return new X509Certificate[0];
    }

    public void checkClientTrusted(
        java.security.cert.X509Certificate[] certs, String authType) {
    }

    public void checkServerTrusted(
        java.security.cert.X509Certificate[] certs, String authType) {
    }
} };
try {
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    sslContextFactory.setSslContext(sc);
    WebSocketClient client = new WebSocketClient(sslContextFactory);

    client.start();
    Future<Session> fut = client.connect(new Main(), URI.create(url));
    Session session = fut.get();
    session.getRemote().sendString("Hello");
    session.getRemote().sendString("155-questions-active");
} catch (Throwable t) {
    t.printStackTrace(System.err);
}
}

@Override
public void onWebSocketConnect(Session sess) {
super.onWebSocketConnect(sess);
System.out.println("Socket Connected: " + sess);
}

@Override
public void onWebSocketText(String message) {
super.onWebSocketText(message);
System.out.println("Received TEXT message: " + message);
}

@Override
public void onWebSocketClose(int statusCode, String reason) {
super.onWebSocketClose(statusCode, reason);
System.out.println("Socket Closed: [" + statusCode + "] " + reason);
}

@Override
public void onWebSocketError(Throwable cause) {
super.onWebSocketError(cause);
cause.printStackTrace(System.err);
}
} 
like image 37
kucing_terbang Avatar answered Oct 01 '22 22:10

kucing_terbang