be.solidx.hot.nio.http.SSLContextBuilder.java Source code

Java tutorial

Introduction

Here is the source code for be.solidx.hot.nio.http.SSLContextBuilder.java

Source

package be.solidx.hot.nio.http;

/*
 * #%L
 * Hot
 * %%
 * Copyright (C) 2010 - 2016 Solidx
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.IOUtils;

import be.solidx.hot.utils.SSLUtils;

public class SSLContextBuilder {

    private static final String JKS = "jks";
    private static final String JKSPASSWORD = "jksPassword";
    private static final String JKS_CERTIFICATE_PASSWORD = "jksCertificatePassword";
    private static final String P12 = "p12";
    private static final String KEY = "key";
    private static final String PASSPHRASE = "passphrase";
    private static final String CERT = "cert";
    private static final String CA = "ca";
    private static final String REJECTUNAUTHORIZED = "rejectUnauthorized";
    private static final String SECUREPROTOCOL = "secureProtocol";

    protected SSLContext buildSSLContext(Map<String, Object> options) throws SSLContextInitializationException {

        Map<String, Object> sslOptions = loadSSLOptions(options);
        KeyManager[] keyManagers = null;
        TrustManager[] trustManagers = null;

        try {
            String secureProtocol = sslOptions.get(SECUREPROTOCOL).toString();
            SSLContext sslContext = SSLContext.getInstance(secureProtocol);

            if (sslOptions.get(JKS) != null) {
                keyManagers = handleClientKeystoreURLProvided(sslOptions).getKeyManagers();
            } else if (sslOptions.get(P12) != null) {
                keyManagers = handleClientPFXURLProvided(sslOptions).getKeyManagers();
            } else if (sslOptions.get(KEY) != null) {
                keyManagers = handleClientKeyCertURLProvided(sslOptions).getKeyManagers();
            }
            trustManagers = handleTrustManagers(sslOptions);
            sslContext.init(keyManagers, trustManagers, null);
            return sslContext;
        } catch (KeyManagementException | NoSuchAlgorithmException | SSLContextInitializationException
                | CertificateException | IOException | URISyntaxException e) {
            throw new SSLContextInitializationException(e);
        }
    }

    private Map<String, Object> loadSSLOptions(Map<String, Object> options) {

        Map<String, Object> parsedOptions = new HashMap<String, Object>();

        if (options.get(CA) != null) {
            parsedOptions.put(CA, options.get(CA));
        }
        if (options.get(CERT) != null) {
            parsedOptions.put(CERT, options.get(CERT));
        }
        if (options.get(JKS) != null) {
            parsedOptions.put(JKS, options.get(JKS));
        }
        if (options.get(JKSPASSWORD) != null) {
            parsedOptions.put(JKSPASSWORD, options.get(JKSPASSWORD));
        } else {
            parsedOptions.put(JKSPASSWORD, "");
        }
        if (options.get(JKS_CERTIFICATE_PASSWORD) != null) {
            parsedOptions.put(JKS_CERTIFICATE_PASSWORD, options.get(JKS_CERTIFICATE_PASSWORD));
        } else {
            parsedOptions.put(JKS_CERTIFICATE_PASSWORD, options.get(JKSPASSWORD));
        }

        if (options.get(KEY) != null) {
            parsedOptions.put(KEY, options.get(KEY));
        }
        if (options.get(PASSPHRASE) != null) {
            parsedOptions.put(PASSPHRASE, options.get(PASSPHRASE));
        }
        if (options.get(P12) != null) {
            parsedOptions.put(P12, options.get(P12));
        }
        if (options.get(REJECTUNAUTHORIZED) != null) {
            if (options.get(REJECTUNAUTHORIZED) instanceof String) {
                parsedOptions.put(REJECTUNAUTHORIZED,
                        Boolean.parseBoolean((String) options.get(REJECTUNAUTHORIZED)));
            } else if (options.get(REJECTUNAUTHORIZED) instanceof Boolean) {
                parsedOptions.put(REJECTUNAUTHORIZED, options.get(REJECTUNAUTHORIZED));
            } else if (options.get(REJECTUNAUTHORIZED) instanceof Integer) {
                Integer integer = (Integer) options.get(REJECTUNAUTHORIZED);
                if (integer > 0) {
                    parsedOptions.put(REJECTUNAUTHORIZED, true);
                } else {
                    parsedOptions.put(REJECTUNAUTHORIZED, false);
                }
            }
        } else {
            parsedOptions.put(REJECTUNAUTHORIZED, true);
        }
        if (options.get(SECUREPROTOCOL) != null) {
            parsedOptions.put(SECUREPROTOCOL, options.get(SECUREPROTOCOL));
        } else {
            parsedOptions.put(SECUREPROTOCOL, "TLSv1.2");
        }
        return parsedOptions;
    }

    private TrustManager[] handleTrustManagers(Map<String, Object> options)
            throws CertificateException, IOException, URISyntaxException {
        boolean rejectUnauthorized = (boolean) options.get(REJECTUNAUTHORIZED);
        if (options.get(CA) != null) {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            return new TrustManager[] { new TrustManager(
                    (X509Certificate) certificateFactory
                            .generateCertificate(getInputStream(new URI(options.get(CA).toString()))),
                    rejectUnauthorized) };
        } else if (!rejectUnauthorized) {
            return new TrustManager[] { new TrustManager(null, rejectUnauthorized) };
        }
        return null;
    }

    private KeyManagerFactory handleClientKeystoreURLProvided(Map<String, Object> options)
            throws SSLContextInitializationException {

        InputStream jksFileInputStream = null;
        try {
            KeyManagerFactory keyManagerFactory = null;

            jksFileInputStream = getInputStream(new URI(options.get(JKS).toString()));
            KeyStore keyStore = KeyStore.getInstance(JKS);
            keyStore.load(jksFileInputStream, options.get(JKSPASSWORD).toString().toCharArray());

            keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, options.get(JKS_CERTIFICATE_PASSWORD).toString().toCharArray());

            return keyManagerFactory;
        } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException | CertificateException
                | URISyntaxException | IOException e) {
            throw new SSLContextInitializationException(e);
        } finally {
            if (jksFileInputStream != null) {
                try {
                    jksFileInputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    private KeyManagerFactory handleClientPFXURLProvided(Map<String, Object> options)
            throws SSLContextInitializationException {

        InputStream jksFileInputStream = null;
        try {
            KeyManagerFactory keyManagerFactory = null;

            jksFileInputStream = getInputStream(new URI(options.get(P12).toString()));
            KeyStore keyStore = KeyStore.getInstance("pkcs12", "SunJSSE");
            keyStore.load(jksFileInputStream, options.get(PASSPHRASE).toString().toCharArray());

            keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, options.get(PASSPHRASE).toString().toCharArray());
            return keyManagerFactory;
        } catch (UnrecoverableKeyException | KeyStoreException | NoSuchProviderException | NoSuchAlgorithmException
                | CertificateException | URISyntaxException | IOException e) {
            throw new SSLContextInitializationException(e);
        } finally {
            if (jksFileInputStream != null) {
                try {
                    jksFileInputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    private KeyManagerFactory handleClientKeyCertURLProvided(Map<String, Object> options)
            throws SSLContextInitializationException {

        InputStream keyInputStream = null;
        InputStream certInputStream = null;

        try {
            KeyManagerFactory keyManagerFactory = null;
            KeyStore keyStore = KeyStore.getInstance(JKS);
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

            byte[] key = IOUtils.toByteArray(getInputStream(new URI(options.get(KEY).toString())));
            Certificate certCertificate = certificateFactory
                    .generateCertificate(getInputStream(new URI(options.get(CERT).toString())));
            char[] password = options.get(PASSPHRASE).toString().toCharArray();

            keyStore.load(null, null);
            // No CA needed, just add pivate key and associated public key to a keystore
            keyStore.setKeyEntry(KEY, SSLUtils.toPrivateKey(new ByteArrayInputStream(key)), password,
                    new Certificate[] { certCertificate });

            keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, password);

            return keyManagerFactory;
        } catch (NullPointerException | UnrecoverableKeyException | KeyStoreException | CertificateException
                | NoSuchAlgorithmException | IOException | URISyntaxException | InvalidKeySpecException e) {
            throw new SSLContextInitializationException(e);
        } finally {
            if (keyInputStream != null) {
                try {
                    keyInputStream.close();
                } catch (IOException e) {
                }
            }
            if (certInputStream != null) {
                try {
                    certInputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    private InputStream getInputStream(URI uri) throws IOException {
        InputStream jksFileInputStream;
        if (uri.getScheme() != null && uri.equals("file")) {
            jksFileInputStream = new FileInputStream(new File(uri));
        } else {
            if (uri.isAbsolute()) {
                jksFileInputStream = getClass().getResourceAsStream(uri.getPath());
            } else {
                jksFileInputStream = getClass().getClassLoader().getResourceAsStream(uri.getPath());
            }
        }
        return jksFileInputStream;
    }

    private static class TrustManager implements X509TrustManager {

        X509Certificate x509Certificate;
        boolean rejectUnauthorized;

        public TrustManager(X509Certificate x509Certificate, boolean rejectUnauthorized) {
            this.x509Certificate = x509Certificate;
            this.rejectUnauthorized = rejectUnauthorized;
        }

        // Unneeded because no client auth
        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkServerTrusted(X509Certificate[] serverCertificates, String authType)
                throws CertificateException {
            if (!rejectUnauthorized)
                return;

            if (serverCertificates.length == 0) {
                throw new CertificateException("Server didn't send any certificate");
            }
            // verify each certificate against CA certificate to find a valid one
            boolean found = false;
            for (X509Certificate x509Certificate : serverCertificates) {
                try {
                    x509Certificate.verify(this.x509Certificate.getPublicKey());
                    found = true;
                    break;
                } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException
                        | SignatureException e) {
                    throw new CertificateException(e);
                }
            }
            if (!found) {
                throw new CertificateException("None of the provided certificates can be validated");
            }
        }
    }

    protected static class SSLContextInitializationException extends Exception {

        private static final long serialVersionUID = -792537019282300095L;

        public SSLContextInitializationException(Throwable cause) {
            super(cause);
        }
    }
}