0

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.

Jordy Baylac
  • 510
  • 6
  • 18

1 Answers1

1

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:

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • But, if I get a Well Signed certificate, that will work? – Jordy Baylac Aug 24 '16 at 21:03
  • 1
    A CA signed certificate that the browser will recognize will be for a specific hostname, and never for `localhost`, so you'll have to keep that in mind. But before you dive into that, consider using some other non-browser websocket client (command line?) to connect to your `wss://` service, just to make sure you have the server side setup properly. – Joakim Erdfelt Aug 24 '16 at 21:14
  • the desktop application run in background, as a windows service, the client will interact with the app if he go to a website that need to communicate with the local app to do its job, because in that site is where the websocket client know how to establish connection. – Jordy Baylac Aug 24 '16 at 21:25
  • I have another cuestion, if I try ajax with http POST method for petitions instead of websocket, the problem will remain? There is something that I can do to solve the situation? please – Jordy Baylac Aug 24 '16 at 21:32
  • 1
    please post an new question, don't continue on this one. – Joakim Erdfelt Aug 24 '16 at 21:48
  • please review http://stackoverflow.com/questions/39173745/ajax-with-http-post-method-for-petitions-instead-of-websocket-in-secure-web-site – Jordy Baylac Aug 26 '16 at 19:51