Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Spring WebSocketClient with SSL?

I connect with my websocket client to non-SSL endpoint without any problem. But I cannot find any way how to connect to wss (SSL) endpoint. Where can I define the SSL factory etc. No object seem to have related set method.

WebSocketClient transport = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(transport);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
String url = cfg.getWebsocketEndpoint();
StompSessionHandler handler = new MySessionHandler();
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
stompClient.connect(url, handler);

I am using wss:// url and on the other side I have a server with self-signed certificate. However, this code does not throw any exception while connecting, but the session is not established.

EDIT: After enabling tracing for web.* I got a standard error, with

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

It occurs when connecting to server with self-signed certificate. However, for RestTemplate I already updated SSLContext with this code and REST calls are fine now, but I do not know why, StandardWebSocketClient is IGNORING the SSLContext. Why?

    String keystoreType = "JKS";
    InputStream keystoreLocation = new FileInputStream("src/main/resources/aaa.jks");
    char [] keystorePassword = "zzz".toCharArray();
    char [] keyPassword = "zzz".toCharArray();

    KeyStore keystore = KeyStore.getInstance(keystoreType);
    keystore.load(keystoreLocation, keystorePassword);
    KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmfactory.init(keystore, keyPassword);

    InputStream truststoreLocation = new FileInputStream("src/main/resources/aaa.jks");
    char [] truststorePassword = "zzz".toCharArray();
    String truststoreType = "JKS";

    KeyStore truststore = KeyStore.getInstance(truststoreType);
    truststore.load(truststoreLocation, truststorePassword);
    TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmfactory.init(truststore);

    KeyManager[] keymanagers = kmfactory.getKeyManagers();
    TrustManager[] trustmanagers =  tmfactory.getTrustManagers();

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keymanagers, trustmanagers, new SecureRandom());
    SSLContext.setDefault(sslContext);

UPDATE: Unfortunately, I did not managed to do this with custom truststore. I installed the certificate with InstallCert.java.

like image 439
Mejmo Avatar asked Aug 24 '15 08:08

Mejmo


2 Answers

I think that each websocket container implementation provides ways to do this. You have to set this configuration using the StandardWebSocketClient .setUserProperties. All those properties are internally set in the ClientEndpointConfig used by the client.

Here's an example with Tomcat as a provider:

StandardWebSocketClient wsClient = //...;
SSLContext sslContext = //...;
wsClient.setUserProperties(WsWebSocketContainer.SSL_CONTEXT_PROPERTY, sslContext);

In any case you should refer to your provider reference documentation to know which configuration keys you should use.

like image 78
Brian Clozel Avatar answered Nov 15 '22 07:11

Brian Clozel


I've faced a similar issue, where I need to use a different ssl context instead of the default one and I could not use the Tomcat provider version solution. I've met a lot of resources and examples about this solution which does not fit my case, and for sure I landed here too on this question.

When you need to use a specific/custom SSLContext to set and you don't need the 'tomcat version', you can go for the Jetty version because it allows you to set which trust store and/or key store you want to use. In this case my Spring application was on the 'client' side and not the server one, but Jetty SslContextFactory provides the same functionalities for the 'Server' case.

The short example code below is for the client side, but it shares methods and signatures with the server side (check the jetty documentation about)

final SslContextFactory.Client factory = new SslContextFactory.Client();
factory.setSslContext(sslContext); // a different loaded java SslContext instead of the default one

//Create the web socket client with jetty client factory
final JettyWebSocketClient client =
      new JettyWebSocketClient(new WebSocketClient(new HttpClient(factory)));
client.start();
final WebSocketStompClient stomp = new WebSocketStompClient(client);

There're also method for setting the trust store or key store instead a fully loaded SslContext.

like image 35
sigur Avatar answered Nov 15 '22 08:11

sigur