0
votes

I have a javascript API that it's included in some websites. For some reasons there is a need for communicate that browser side with a desktop application written in Java. As you can see, all is local to the host.

My first solution was to use plugins/extensions for each known browser, but the lack of good support and the bunch of dependencies stop me to opt for that option. Then my second approach was to use websockets in both sides, with the server in the Java app, and the client being the browser, everything will going to be perfect, but then a big problem come to the surface: HTTPS websites can't establish connection with a insecure websocket server, not matter of Java, and not matter of if the server is on the localhost.

The java_websocket API that I've been using was not good enough to resolve this problem, and in google Jetty is a good name that sound for my purposes. I get involve with Jetty in few hours, and after some examples I got two classes, a server endpoint (EchoSocket) and the server (EventServer):

EventServer.java

import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.ServerContainer;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;

public class EventServer {

  public static void main(String[] args) {

      try {
        Server server = new Server();
        int httpsPort = 8771;

        SslContextFactory scf = new SslContextFactory("mykeystore.jks");
        scf.setKeyStorePassword("blaStore");
        scf.setKeyManagerPassword("blablaKey");

        SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(scf, "http/1.1");

        HttpConfiguration https = new HttpConfiguration();
        https.setSecurePort(httpsPort);
        https.setSecureScheme("https");
        https.addCustomizer(new SecureRequestCustomizer());

        ServerConnector sslConnector = new ServerConnector(server,
                sslConnectionFactory,
                new HttpConnectionFactory(https)
        );
        sslConnector.setPort(httpsPort);
        server.addConnector(sslConnector);

        HandlerList baseHandler = new HandlerList();
        server.setHandler(baseHandler);

        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); //ServletContextHandler.SESSIONS
        context.setContextPath("/");
        baseHandler.addHandler(context);

        ServerContainer jsrContainer = WebSocketServerContainerInitializer.configureContext(context);
        jsrContainer.addEndpoint(EchoSocket.class);
        baseHandler.addHandler(new DefaultHandler());

        server.start();
        server.dump(System.err);
        server.join();

      } catch (Throwable ex) {
         ex.printStackTrace(System.err);
     }
  }
}

EchoSocket.java

import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value="/echo")
public class EchoSocket
{
    @OnMessage
    public void onMessage(javax.websocket.Session session, String message){
        session.getAsyncRemote().sendText("welcomeeee");
    }

    @OnError
    public void onError(Throwable cause)
    {
        cause.printStackTrace(System.err);
    }

}

Using a Websocket online test I can communicate from Mozilla Firefox and Opera, but the connection it's disconnected after some minutes, and in some times there is an undefined error that close the connection.

In case of Microsoft Edge and IE nothing works, when I wrote the URI as wss://localhost:8771/echo the error is:

SCRIPT12017: WebSocket Error: SECURITY_ERR, Cross zone connection not allowed

And when I try with wss://127.0.0.1:8771/echo the error is:

SCRIPT12038: WebSocket Error: Network Error 12038, The host name in the certificate is invalid or does not match

In Google Chrome neither way of URI seems work, there is not connection, the error is:

WebSocket connection to 'wss://127.0.0.1:8771/echo' failed: Error in connection establishment: net::ERR_NETWORK_CHANGED

and in the other case:

WebSocket connection to 'wss://localhost:8771/echo' failed: Error in connection establishment: net::ERR_CONNECTION_CLOSED

So, I need some help, but may be you can answer me some of the next questions:

  • Is correct my example of Jetty?, if not, How can I correct it?
  • Should I try another libraries, or older versions of Jetty to make this work in most of the browsers?
  • Should I see other implementations of a websocket server in another languages like C#?
  • What hostname or SubjectAltName do I have to set in my SSL certificate to allow connections from IE and Edge? Maybe "localhost", or "127.0.0.1" .

Please, thanks in advance, and sorry by my english.

1

1 Answers

1
votes

Based on experience, WebSocket in a browser, via JavaScript is not possible to use with a wss:// encrypted host that the browser is unaware of (Certificate is not signed by a CA that the browser is using)

Self-Signed is not possible, unless you add the certificate chain to your browser too.

This is mainly because its an API, and as such has no GUI to allow you to accept the Certificate to bypass the security restrictions in the browser (for certificates that are not part of a CA that it knows about)

Note: Sometimes (but not always) you can get around this certificate restriction, temporarily, by having a normal https://localhost/index.html lookup before you attempt to access the wss://localhost..., allowing you to accept the Certificate for that session.

Some other news: