Is it possible to change plain socket to SSLSocket?

There is a plain socket server listening on port 12345;

ServerSocket s = new ServerSocket(12345);

What I want to know is that it is possible that:

  1. If the client send a http request, the server handle the request directly,
  2. If the client send a https request, the server change client socket to SSLSocket?

Thanks


Is it possible to change plain socket to SSLSocket?

Yes, it is. On the server side, the following works:

ServerSocketFactory ssf = ServerSocketFactory.getDefault();
ServerSocket serverSocket = ssf.createServerSocket(12345);

// I've initialised an sslContext with a keystore, as you normally would.
Socket socket = serverSocket.accept();
SSLSocketFactory sslSf = sslContext.getSocketFactory();
// The host name doesn't really matter, since we're turning it into a server socket
// (No need to match the host name to the certificate on this side).
SSLSocket sslSocket = (SSLSocket) sslSf.createSocket(socket, null,
    socket.getPort(), false);
sslSocket.setUseClientMode(false);

// Use the sslSocket InputStream/OutputStream as usual.

SSLSocketFactory.createSocket(Socket, ...) will by default convert the existing Socket into a client-mode SSLSocket. Since the handshake only starts when you start reading/writing with the I/O streams, it's still time to change the mode using setUseClientMode(false).

Regarding the rest of the question:

What I want to know is that it is possible that:

  • If the client send a http request, the server handle the request directly,
  • If the client send a https request, the server change client socket to SSLSocket?

Again, yes, it's possible. It's sometimes referred to as "port unification" and it's implemented in Grizzly and thus Glassfish.

It works because both HTTP and TLS (upon which HTTPS works) are protocols where the client is expected to talk first. Therefore, the server can detect whether what the client initially sends is a TLS ClientHello message (in which case it should try to proceed with the TLS handshake) or a plain HTTP request (e.g. GET / HTTP/1.1...).

I suspect port unification is "easier" to do using SSLEngine, otherwise, it might be hard to implement a read-ahead on a plain socket, which you would still be able to convert via SSLSocketFactory.createSocket(Socket, ...).

Note that this is still rather unusual, though.


It is not possible to serve both http and https on the same port. However, it should in theory be possible to upgrade an existing http connection to use TLS if both the client and server supports it, see RFC 2817. The SSLSocketFactory class has a createSocket() function which can be used to upgrade an existing socket to SSL/TLS.

Disclaimer: I have not attempted to do this, and implementing RFC 2817 is likely non-trivial.

Edit: Apparently I was wrong, like Bruno wrote it is possible to serve http and https on the same port using a technology called port unification, which uses the first few bytes received to detect the protocol. There is an example of this included with the Netty (JBoss) framework.