1

I have a spring-boot application and I am trying to open a secure websocket connection to a server, but I keep getting an error due to: No subject alternative names present.

Here is what I have tried:

StandardWebSocketClient client = new StandardWebSocketClient();
SSLContext sslContext = this.getSslContext("path/to/truststore", "path/to/keystore.p12", "password");
client.getUserProperties().put(SSL_CONTEXT_PROPERTY, sslContext);
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
headers.add("Sec-WebSocket-Key", "SGVsbG8sIHdvcmxkIQ==");
headers.add("Origin", "https://192.168.1.132:8445");
headers.add("Sec-WebSocket-Version", "13");
String url = "wss://192.168.1.132:8445/websocket";
URI uri = new URI(url);
ListenableFuture<WebSocketSession> future = client.doHandshake(new AbstractWebSocketHandler() {
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        receivedMessages += message.getPayload();
        System.out.println(message.getPayload());
    }
}, headers, uri);
WebSocketSession socketSession = future.get();

And here is the getSslContext method:

private SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
        throws GeneralSecurityException, IOException {
    KeyStore keystore = KeyStore.getInstance("JKS");
    try (InputStream in = new FileInputStream(keystoreFile)) {
        keystore.load(in, password.toCharArray());
    }
    try (InputStream in = new FileInputStream(trustStoreFile)) {
        keystore.load(in, password.toCharArray());
    }

    KeyManagerFactory keyManagerFactory =
            KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keystore, password.toCharArray());

    TrustManagerFactory trustManagerFactory =
            TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(keystore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(
            keyManagerFactory.getKeyManagers(),
            trustManagerFactory.getTrustManagers(),
            new SecureRandom());

    return sslContext;
}

Given this code, I am always getting the following error:

Caused by: java.security.cert.CertificateException: No subject alternative names present
    at java.base/sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:141)
    at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:100)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:429)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:141)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1302)

I have also tried to replicate the request using the following curl command:

curl --include --no-buffer --header "Connection: Upgrade" --header "Upgrade: websocket" --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" --header "Origin: https://192.168.1.132:8445" --header "Sec-WebSocket-Version: 13" -Ss --cacert myCA.pem --cert SSLCert.pem:`cat SSLCert.pass` https://192.168.1.132:8445/websocket

The handshake seems to be successful:

HTTP/1.1 101
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
X-Application-Context: panel-command-server:default,postgres,cassandra,kafka:8080
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: qGEgH3En71di5rrssAZTmtRTyFk=

Any ideas on what might be the reason behind the No subject alternative names present error from the spring application?

Kaleen
  • 51
  • 6
  • Possible duplicate of [How to fix the "java.security.cert.CertificateException: No subject alternative names present" error?](https://stackoverflow.com/questions/19540289/how-to-fix-the-java-security-cert-certificateexception-no-subject-alternative) – deon cagadoes Jul 29 '19 at 09:01
  • It is pretty much the same, but none of the answers work. – Kaleen Jul 29 '19 at 12:42
  • What is the line when crash ? – deon cagadoes Jul 29 '19 at 17:10
  • It is the last line, at: WebSocketSession socketSession = future.get(); – Kaleen Jul 30 '19 at 11:46
  • I did manage to find a workaround, if you check my answer below. – Kaleen Jul 30 '19 at 11:47
  • Please explainme from where you get ``client.doHandshake()`` ? this function doesn't exist on spring documentation, have you try this: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/socket/client/standard/StandardWebSocketClient.html#doHandshakeInternal-org.springframework.web.socket.WebSocketHandler-org.springframework.http.HttpHeaders-java.net.URI-java.util.List-java.util.List-java.util.Map- – deon cagadoes Jul 30 '19 at 16:22
  • Actually, there is a doHandshake method. It is inherited from the AbstractWebSocketClient class. – Kaleen Jul 31 '19 at 14:42
  • Yes its true! https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/socket/client/AbstractWebSocketClient.html#doHandshake-org.springframework.web.socket.WebSocketHandler-java.lang.String-java.lang.Object...- – deon cagadoes Aug 01 '19 at 08:59

1 Answers1

3

Managed to fix the issue by overriding some methods from the trust manager to bypass the checks like in this answer: How to bypass ssl certificate checking in java.

This is the final version of the getSslContext I used:

    private SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
            throws GeneralSecurityException, IOException {
        KeyStore keystore = KeyStore.getInstance("JKS");
        try (InputStream in = new FileInputStream(keystoreFile)) {
            keystore.load(in, password.toCharArray());
        }
        try (InputStream in = new FileInputStream(trustStoreFile)) {
            keystore.load(in, password.toCharArray());
        }

        KeyManagerFactory keyManagerFactory =
                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keystore, password.toCharArray());

        TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keystore);

        // Get hold of the default trust manager
        X509TrustManager x509Tm = null;
        for (TrustManager tm : tmf.getTrustManagers()) {
            if (tm instanceof X509TrustManager) {
                x509Tm = (X509TrustManager) tm;
                break;
            }
        }

        // Wrap it in your own class.
        final X509TrustManager finalTm = x509Tm;
        X509ExtendedTrustManager customTm = new X509ExtendedTrustManager() {
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

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

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

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {

            }

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {

            }
        };

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(
                keyManagerFactory.getKeyManagers(),
                new TrustManager[]{customTm},
                new SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        return sslContext;
    }
Kaleen
  • 51
  • 6