Skip to content

Client connects forever with SSL/WSS #899

@bergice

Description

@bergice

Describe what you would like to know or do

  • Why does my client get stuck connecting forever to my WSS server?
  • Is it possible to achieve this by setting up a DNS rule from my domain to route to the server IP, thus eliminating the need for coding the SSL support to the server directly?
  • Do I need to do something on the client end other than connect via WSS to get this working? The client will be connecting via browser.

Describe the solution you'd considered

  • Connecting without WSS works fine.

Additional context

  • Server OS: Amazon Linux AMI, macOS Mojave
  • Client OS: macOS Mojave
  • JRE: 1.8
  • WS Server: org.java-websocket:Java-WebSocket:1.4.0
  • WS Client: com.github.czyzby:gdx-websocket-common:1.9.1.9.6

I'm thinking this has to do with the way I'm creating the SSL context.

The public/private pem files are retrieved directly from my registrar (no certbot).

Here is a replicateable example:

ServerExample.java

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.DefaultSSLWebSocketServerFactory;
import org.java_websocket.server.WebSocketServer;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

public class ServerExample {

	private static byte[] parseDERFromPEM( byte[] pem, String beginDelimiter, String endDelimiter ) {
		String data = new String( pem );
		String[] tokens = data.split( beginDelimiter );
		tokens = tokens[1].split( endDelimiter );
		return DatatypeConverter.parseBase64Binary( tokens[0] );
	}

	private static RSAPrivateKey generatePrivateKeyFromDER( byte[] keyBytes ) throws InvalidKeySpecException, NoSuchAlgorithmException {
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec( keyBytes );

		KeyFactory factory = KeyFactory.getInstance( "RSA" );

		return ( RSAPrivateKey ) factory.generatePrivate( spec );
	}

	private static X509Certificate generateCertificateFromDER( byte[] certBytes ) throws CertificateException {
		CertificateFactory factory = CertificateFactory.getInstance( "X.509" );

		return ( X509Certificate ) factory.generateCertificate( new ByteArrayInputStream( certBytes ) );
	}

	public static SSLContext getContext(byte[] certBytes, byte[] keyBytes) {
		SSLContext context;
		char[] password = new char[0];

		try {
			// create certificates
			X509Certificate cert = generateCertificateFromDER( parseDERFromPEM(certBytes, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----" ) );
			RSAPrivateKey key = generatePrivateKeyFromDER( parseDERFromPEM(keyBytes, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----" ) );

			// create KeyStore
			KeyStore keystore = KeyStore.getInstance( "JKS" );
			keystore.load( null );
			keystore.setCertificateEntry( "cert-alias", cert );
			keystore.setKeyEntry( "key-alias", key, password, new Certificate[]{ cert } );

//            // create TrustManager
//            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
//            tmf.init(keystore);

			// create KeyManager
			KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
			kmf.init( keystore, password );

			// create the SSL context
			context = SSLContext.getInstance( "TLS" );
			context.init( kmf.getKeyManagers(), /*tmf.getTrustManagers()*/null, null );
		} catch ( Exception e ) {
			context = null;
		}

		return context;
	}

	public static void main (String[] strArgs) throws Exception {

		// Create the server
		WebSocketServer server = new WebSocketServer(new InetSocketAddress(6767)) {
			@Override
			public void onOpen(WebSocket conn, ClientHandshake handshake) {
				System.out.println("Connection opened " + conn.getRemoteSocketAddress().getHostName());
			}

			@Override
			public void onClose(WebSocket conn, int code, String reason, boolean remote) {
				System.out.println("Connection closed " + conn.getRemoteSocketAddress().getHostName());
			}

			@Override
			public void onMessage(WebSocket conn, String message) {
				System.out.println(conn.getRemoteSocketAddress().getHostName() + " says " + message);
			}

			@Override
			public void onError(WebSocket conn, Exception ex) {
				System.out.println("Error from " + conn.getRemoteSocketAddress().getHostName() + ": " + ex.getMessage());
			}

			@Override
			public void onStart() {
				System.out.println("Started");
			}
		};

		// Setup SSL - comment this section out to see this example working without WSS
		byte[] certBytes = Files.readAllBytes(Paths.get("cert.pem"));
		byte[] keyBytes = Files.readAllBytes(Paths.get("key.pem"));

		server.setWebSocketFactory( new DefaultSSLWebSocketServerFactory( getContext(certBytes, keyBytes) ) );

		// Set timeout
		server.setConnectionLostTimeout( 30 );

		// Set reuse address - to avoid `Address already in use`
		server.setReuseAddr(true);

		// Start the server
		server.start();
	}
}

ClientExample.java

import com.github.czyzby.websocket.CommonWebSockets;
import com.github.czyzby.websocket.WebSocket;
import com.github.czyzby.websocket.net.ExtendedNet;

public class ClientExample {

	public static void main (String[] arg) {
		CommonWebSockets.initiate();
//		WebSocket socket = ExtendedNet.getNet().newWebSocket("localhost", 6767); // this works
		WebSocket socket = ExtendedNet.getNet().newSecureWebSocket("localhost", 6767); // this does not work
		socket.connect();
		socket.send("Hello, world!");
		socket.close();
	}
}

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions