-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Closed
Description
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();
}
}