SSL Support in CoralReactor through SSLSocketChannel

CoralReactor implements its own non-blocking SSLSocketChannel so you can have out-of-box support for SSL. In this article we show how you can connect to a SSL server (https, wss, etc.) easily with a CoralReactor client.

A Simple HTTP Client

For plain-text HTTP port 80 it is extremely easy. For example, to connect to www.google.com and fetch the HTTP response headers you can write the simple cliente below:

package com.coralblocks.coralreactor.client.ssl;

import static com.coralblocks.corallog.Log.*;

import java.net.URL;
import java.nio.ByteBuffer;

import com.coralblocks.coralbits.util.ByteBufferUtils;
import com.coralblocks.coralreactor.client.AbstractLineTcpClient;
import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.nio.NioReactor;

public class SSLClient extends AbstractLineTcpClient {

	public SSLClient(NioReactor nio, String host, int port) {
		super(nio, host, port);
	}

	@Override
	protected void handleConnectionOpened() {
		send("GET / HTTP/1.0\n");
	}

	@Override
	protected void handleMessage(ByteBuffer msg) {
		// only print the HTTP response headers
		String s = ByteBufferUtils.parseString(msg);
		if (s.startsWith("<")) {
			close();
		} else {
			System.out.println(s);
		}
	}

	public static void main(String[] args) throws Exception {
		
		URL url = new URL("http://www.google.com"); // note we are using HTTP (port 80)
		String proto = url.getProtocol();
		String host = url.getHost();
		int port = url.getDefaultPort();

		NioReactor nio = NioReactor.create();
		
		Info.log("Connecting...", "url=", url, "host=", host, "port=", port, "proto=", proto);
		
		final Client client = new SSLClient(nio, host, port);
		client.open();

		nio.start();
	}
}

And the output:

22:13:40.350783-INFO Connecting... url=http://www.google.com host=www.google.com port=80 proto=http
22:13:40.372449-INFO SSLClient-www.google.com:80 Client opened! sequence=1 session=null
22:13:40.390583-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
22:13:40.595306-INFO SSLClient-www.google.com:80 Connection established!
22:13:40.595489-INFO SSLClient-www.google.com:80 Connection opened!
HTTP/1.0 200 OK
Date: Thu, 10 Sep 2015 02:13:40 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Set-Cookie: PREF=ID=1111111111111111:FF=0:TM=1441851220:LM=1441851220:V=1:S=XSWC8Y8PJthovQv9; expires=Thu, 31-Dec-2015 16:02:17 GMT; path=/; domain=.google.com
Set-Cookie: NID=71=uGm6eP_jn9OmofaZ4RX10EFlALI8NmfX9jnfSiLrNlWPngHQdR1q_pl2QifvtlJJBmPi6_Dmoacg8pP2A8TgifnQ7EIxQAOoR15DkRohQrBUKn1nFauUkoUwSwgIKgPv; expires=Fri, 11-Mar-2016 02:13:40 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding

22:13:40.839168-INFO SSLClient-www.google.com:80 Client was shutdown
22:13:40.839329-INFO SSLClient-www.google.com:80 Client closed!


Switching to HTTPS

CoralReactor will automatically connect to the server to download and install the required SSL certificates so that you don’t have to worry about anything to make SSL work. All you have to do is turn it on with a config parameter (i.e. useSSL) and pass the appropriate SSL port (i.e. 443) to you client:

	public static void main(String[] args) throws Exception {
		
		URL url = new URL("https://www.google.com"); // note we are using HTTPS now (port 443)
		String proto = url.getProtocol();
		String host = url.getHost();
		int port = url.getDefaultPort();

		NioReactor nio = NioReactor.create();
		MapConfiguration config = new MapConfiguration();
		config.add("useSSL", true); // tell CoralReactor that we want to use SSL
		
		Info.log("Connecting...", "url=", url, "host=", host, "port=", port, "proto=", proto);
		
		final Client client = new SSLClient(nio, host, port, config);
		client.open();

		nio.start();
	}

And the output:

22:51:00.100277-INFO Connecting... url=https://www.google.com host=www.google.com port=443 proto=https
22:51:00.118394-INFO SSLClient-www.google.com:443 Client opened! sequence=1 session=null
22:51:00.880822-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
22:51:01.055298-INFO SSLClient-www.google.com:443 Connection established!
22:51:01.055608-INFO SSLClient-www.google.com:443 Connection opened!
HTTP/1.0 200 OK
Date: Thu, 10 Sep 2015 02:51:01 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Set-Cookie: PREF=ID=1111111111111111:FF=0:TM=1441853461:LM=1441853461:V=1:S=W287StjqyNrsY-rC; expires=Thu, 31-Dec-2015 16:02:17 GMT; path=/; domain=.google.com
Set-Cookie: NID=71=Z-T634Zo9qGSn9GbdTlmX5KFeU6ZZrzVySqrtfJWuD_nwFbo8Qlsm3EzeRCgiybqBmnW7Mkmn0IdGTVgv6nMaUrX3YtvfsRzQH-FgmJAzGpVv1y9WV2DaLa3UNPgY1uY; expires=Fri, 11-Mar-2016 02:51:01 GMT; path=/; domain=.google.com; HttpOnly
Alternate-Protocol: 443:quic,p=1
Alt-Svc: quic=":443"; p="1"; ma=604800
Accept-Ranges: none
Vary: Accept-Encoding

22:51:01.283551-INFO SSLClient-www.google.com:443 Client was shutdown
22:51:01.283760-INFO SSLClient-www.google.com:443 Client closed!


More Control (only if you need to)

CoralReactor uses javax.net.ssl.SSLEngine under the hood as specified here to implement its SSL support and encrypt the socket communication. If you want more control you can download and install the SSL certificates yourself (i.e. manually) and tell CoralReactor where to find them. These can be done with openssl and keytool.

For example to download the certificate from google you can do:

$ echo "" | openssl s_client -connect www.google.com:443 -showcerts 2>/dev/null | openssl x509 -out google.cer

To add the google.cer you downloaded above to a keystore so it can be passed to a CoralReactor client you can do:

$ keytool -import -file google.cer -alias google -keystore google.jks -storepass "abc123" -keypass "abc123"

Now you can inform the CoralReactor client that you want to use the google.jks keystore. See below:

	public static void main(String[] args) throws Exception {
		
		URL url = new URL("https://www.google.com"); // note we are using HTTPS now
		String proto = url.getProtocol();
		String host = url.getHost();
		int port = url.getDefaultPort();

		NioReactor nio = NioReactor.create();
		MapConfiguration config = new MapConfiguration();
		config.add("useSSL", true); // tell CoralReactor that we want to use SSL
		config.add("sslKeyStoreFile", "/path/to/google.jks"); // path to keystore file
		config.add("sslKeyPassword", "abc123"); // key password
		config.add("sslKeyStorePassword", "abc123"); // keystore password
		
		Info.log("Connecting...", "url=", url, "host=", host, "port=", port, "proto=", proto);
		
		final Client client = new SSLClient(nio, host, port, config);
		client.open();

		nio.start();
	}


Using Stunnel as a SSL Proxy

For a cleaner alternative you can also use stunnel as a SSL proxy. For example to connect to google.com:443 use the stunnel.conf file below:

[remote]
client = yes
accept = 8888
connect = www.google.com:443

Then run stunnel:

$ sudo stunnel stunnel.conf

You can easily test with netcat with the command-line below:

$ cat <(echo -e "GET / HTTP/1.0\n") | nc localhost 8888 | head -n 9
HTTP/1.0 200 OK
Date: Thu, 10 Sep 2015 03:09:13 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN