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
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.
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.
There's a much simpler way, no need to mess with truststores, or custom
X509TrustManagers
, or customizedSSLContexts
.Just use what
SslContextFactory
comes with already ...Which will have results like this ...
Here's my Tyrus-java client using stub that I used to connect to my Jetty 9.3.6 server running HTTPS with a self-signed certificate (adapted from Tyrus websocket client, Section 8.1):
Where
SillyWebSocketClient sillyWebSocketClient
extendsjavax.websocket.Endpoint
.I am using a Java 8/Gradle 2.7 environment, and my
build.gradle
looks like this:Hope this helps.