org.cloudcoder.submitsvc.oop.builder.WebappSocketFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudcoder.submitsvc.oop.builder.WebappSocketFactory.java

Source

// CloudCoder - a web-based pedagogical programming environment
// Copyright (C) 2011-2012, Jaime Spacco <jspacco@knox.edu>
// Copyright (C) 2011-2012, David H. Hovemeyer <dhovemey@ycp.edu>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package org.cloudcoder.submitsvc.oop.builder;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A WebappSocketFactory object creates secure connections to the webapp.
 * 
 * @author Jaime Spacco
 * @author David Hovemeyer
 */
public class WebappSocketFactory {
    private static final Logger logger = LoggerFactory.getLogger(WebappSocketFactory.class);

    private String host;
    private int port;
    private String keystoreFilename;
    private String keystorePassword;
    private SSLSocketFactory socketFactory;

    /**
     * Constructor.
     * 
     * @param host               the host on which the webapp is running
     * @param port               the port the webapp is using to listen for connections from builders
     * @param keystoreFilename   the name of the keystore file containing the key(s) needed
     *                           for secure communication with the webapp
     * @param keystorePassword   the keystore password
     * @throws IOException
     * @throws GeneralSecurityException
     */
    public WebappSocketFactory(String host, int port, String keystoreFilename, String keystorePassword)
            throws IOException, GeneralSecurityException {
        this.host = host;
        this.port = port;
        this.keystoreFilename = keystoreFilename;
        this.keystorePassword = keystorePassword;
        if (this.getClass().getClassLoader().getResource(keystoreFilename) == null) {
            //XXX this is a hack so that we can distribute jarfiles that communicate securely
            //TODO better documentation of generating new keystores
            // also should figure out how to generate keystores from Java programmatically
            this.keystoreFilename = "defaultkeystore.jks";
            this.keystorePassword = "changeit";
        }
        this.socketFactory = createSocketFactory();
        logger.info("Builder: using keystore {}", this.keystoreFilename);
    }

    private SSLSocketFactory createSocketFactory() throws IOException, GeneralSecurityException {
        String keyStoreType = "JKS";
        InputStream keyStoreInputStream = this.getClass().getClassLoader().getResourceAsStream(keystoreFilename);
        if (keyStoreInputStream == null) {
            throw new IOException("Could not load keystore " + keystoreFilename);
        }

        KeyStore keyStore;
        try {
            keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(keyStoreInputStream, keystorePassword.toCharArray());
        } finally {
            IOUtils.closeQuietly(keyStoreInputStream);
        }

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
        //trustManagerFactory.init(trustStore);
        // XXX Load the cert (public key) here instead of the private key?
        trustManagerFactory.init(keyStore);

        // TrustManager
        X509TrustManager x509TrustManager = null;
        for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
            if (trustManager instanceof X509TrustManager) {
                x509TrustManager = (X509TrustManager) trustManager;
                break;
            }
        }
        if (x509TrustManager == null) {
            throw new IllegalArgumentException("Cannot find x509TrustManager");
        }

        // KeyManager
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509", "SunJSSE");
        keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
        X509KeyManager x509KeyManager = null;
        for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
            if (keyManager instanceof X509KeyManager) {
                x509KeyManager = (X509KeyManager) keyManager;
                break;
            }
        }
        if (x509KeyManager == null) {
            throw new NullPointerException();
        }

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(new KeyManager[] { x509KeyManager }, new TrustManager[] { x509TrustManager }, null);

        return sslContext.getSocketFactory();
    }

    /**
     * Create a secure connection to the webapp.
     * 
     * @return Socket through which the builder can communicate with the webapp
     * @throws UnknownHostException
     * @throws IOException
     * @throws GeneralSecurityException
     */
    public Socket connectToWebapp() throws UnknownHostException, IOException {
        SSLSocket socket = (SSLSocket) socketFactory.createSocket(host, port);
        socket.setEnabledProtocols(new String[] { "TLSv1" });
        return socket;
    }
}