org.jasig.cas.authentication.FileTrustStoreSslSocketFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.cas.authentication.FileTrustStoreSslSocketFactory.java

Source

/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.jasig.cas.authentication;

import org.apache.commons.io.IOUtils;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * The SSL socket factory that loads the SSL context from a custom
 * truststore file strictly used ssl handshakes for proxy authentication. 
 * @author Misagh Moayyed
 * @since 4.1.0
 */
public final class FileTrustStoreSslSocketFactory extends SSLConnectionSocketFactory {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileTrustStoreSslSocketFactory.class);

    /**
     * Instantiates a new trusted proxy authentication trust store ssl socket factory.
     * Defaults to <code>TLSv1</code> and {@link SSLConnectionSocketFactory#BROWSER_COMPATIBLE_HOSTNAME_VERIFIER}
     * for the supported protocols and hostname verification.
     * @param trustStoreFile the trust store file
     * @param trustStorePassword the trust store password
     */
    public FileTrustStoreSslSocketFactory(final File trustStoreFile, final String trustStorePassword) {
        this(trustStoreFile, trustStorePassword, KeyStore.getDefaultType());
    }

    /**
     * Instantiates a new trusted proxy authentication trust store ssl socket factory.
     * @param trustStoreFile the trust store file
     * @param trustStorePassword the trust store password
     * @param trustStoreType the trust store type
     */
    public FileTrustStoreSslSocketFactory(final File trustStoreFile, final String trustStorePassword,
            final String trustStoreType) {
        super(getTrustedSslContext(trustStoreFile, trustStorePassword, trustStoreType));
    }

    /**
     * Gets the trusted ssl context.
     *
     * @param trustStoreFile the trust store file
     * @param trustStorePassword the trust store password
     * @param trustStoreType the trust store type
     * @return the trusted ssl context
     */
    private static SSLContext getTrustedSslContext(final File trustStoreFile, final String trustStorePassword,
            final String trustStoreType) {
        try {

            if (!trustStoreFile.exists() || !trustStoreFile.canRead()) {
                throw new FileNotFoundException(
                        "Truststore file cannot be located at " + trustStoreFile.getCanonicalPath());
            }
            final FileInputStream casStream = new FileInputStream(trustStoreFile);
            final KeyStore casTrustStore = KeyStore.getInstance(trustStoreType);
            final char[] trustStorePasswordCharArray = trustStorePassword.toCharArray();

            casTrustStore.load(casStream, trustStorePasswordCharArray);
            IOUtils.closeQuietly(casStream);

            final String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
            final X509KeyManager customKeyManager = getKeyManager("PKIX", casTrustStore,
                    trustStorePasswordCharArray);
            final X509KeyManager jvmKeyManager = getKeyManager(defaultAlgorithm, null, null);
            final X509TrustManager customTrustManager = getTrustManager("PKIX", casTrustStore);
            final X509TrustManager jvmTrustManager = getTrustManager(defaultAlgorithm, null);

            final KeyManager[] keyManagers = {
                    new CompositeX509KeyManager(Arrays.asList(jvmKeyManager, customKeyManager)) };
            final TrustManager[] trustManagers = {
                    new CompositeX509TrustManager(Arrays.asList(jvmTrustManager, customTrustManager)) };

            final SSLContext context = SSLContexts.custom().useSSL().build();
            context.init(keyManagers, trustManagers, null);
            return context;

        } catch (final Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets key manager.
     *
     * @param algorithm the algorithm
     * @param keystore the keystore
     * @param password the password
     * @return the key manager
     * @throws Exception the exception
     */
    private static X509KeyManager getKeyManager(final String algorithm, final KeyStore keystore,
            final char[] password) throws Exception {
        final KeyManagerFactory factory = KeyManagerFactory.getInstance(algorithm);
        factory.init(keystore, password);
        return (X509KeyManager) factory.getKeyManagers()[0];
    }

    /**
     * Gets trust manager.
     *
     * @param algorithm the algorithm
     * @param keystore the keystore
     * @return the trust manager
     * @throws Exception the exception
     */
    private static X509TrustManager getTrustManager(final String algorithm, final KeyStore keystore)
            throws Exception {
        final TrustManagerFactory factory = TrustManagerFactory.getInstance(algorithm);
        factory.init(keystore);
        return (X509TrustManager) factory.getTrustManagers()[0];
    }

    private static class DoesNotTrustStrategy implements TrustStrategy {
        @Override
        public boolean isTrusted(final X509Certificate[] x509Certificates, final String s)
                throws CertificateException {
            return false;
        }
    }

    private static class CompositeX509KeyManager implements X509KeyManager {

        private final List<X509KeyManager> keyManagers;

        /**
         * Represents an ordered list of {@link X509KeyManager}s with most-preferred managers first.
         * @param keyManagers list of key managers
         */
        public CompositeX509KeyManager(final List<X509KeyManager> keyManagers) {
            this.keyManagers = keyManagers;
        }

        @Override
        public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) {
            for (X509KeyManager keyManager : keyManagers) {
                final String alias = keyManager.chooseClientAlias(keyType, issuers, socket);
                if (alias != null) {
                    return alias;
                }
            }
            return null;
        }

        @Override
        public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) {
            for (X509KeyManager keyManager : keyManagers) {
                final String alias = keyManager.chooseServerAlias(keyType, issuers, socket);
                if (alias != null) {
                    return alias;
                }
            }
            return null;
        }

        @Override
        public PrivateKey getPrivateKey(final String alias) {
            for (X509KeyManager keyManager : keyManagers) {
                final PrivateKey privateKey = keyManager.getPrivateKey(alias);
                if (privateKey != null) {
                    return privateKey;
                }
            }
            return null;
        }

        @Override
        public X509Certificate[] getCertificateChain(final String alias) {
            for (X509KeyManager keyManager : keyManagers) {
                final X509Certificate[] chain = keyManager.getCertificateChain(alias);
                if (chain != null && chain.length > 0) {
                    return chain;
                }
            }
            return null;
        }

        @Override
        public String[] getClientAliases(final String keyType, final Principal[] issuers) {
            final List<String> aliases = new ArrayList<String>();
            for (X509KeyManager keyManager : keyManagers) {
                final List<String> list = Arrays.asList(keyManager.getClientAliases(keyType, issuers));
                aliases.addAll(list);
            }
            return aliases.toArray(new String[] {});
        }

        @Override
        public String[] getServerAliases(final String keyType, final Principal[] issuers) {
            final List<String> aliases = new ArrayList<String>();
            for (X509KeyManager keyManager : keyManagers) {
                final List<String> list = Arrays.asList(keyManager.getServerAliases(keyType, issuers));
                aliases.addAll(list);
            }
            return aliases.toArray(new String[] {});
        }

    }

    /**
     * Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the
     * composed managers trusts a certificate chain, then it is trusted by the composite manager.
     */
    private static class CompositeX509TrustManager implements X509TrustManager {

        private static final Logger LOGGER = LoggerFactory.getLogger(CompositeX509TrustManager.class);

        private final List<X509TrustManager> trustManagers;

        /**
         * Instantiates a new Composite x 509 trust manager.
         *
         * @param trustManagers the trust managers
         */
        public CompositeX509TrustManager(final List<X509TrustManager> trustManagers) {
            this.trustManagers = trustManagers;
        }

        @Override
        public void checkClientTrusted(final X509Certificate[] chain, final String authType)
                throws CertificateException {
            for (X509TrustManager trustManager : trustManagers) {
                try {
                    trustManager.checkClientTrusted(chain, authType);
                    return;
                } catch (final CertificateException e) {
                    LOGGER.debug(e.getMessage(), e);
                }
            }
            throw new CertificateException("None of the TrustManagers trust this certificate chain");
        }

        @Override
        public void checkServerTrusted(final X509Certificate[] chain, final String authType)
                throws CertificateException {
            for (final X509TrustManager trustManager : trustManagers) {
                try {
                    trustManager.checkServerTrusted(chain, authType);
                    return;
                } catch (final CertificateException e) {
                    LOGGER.debug(e.getMessage(), e);
                }
            }
            throw new CertificateException("None of the TrustManagers trust this certificate chain");
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            final List<X509Certificate> certificates = new ArrayList<>();
            for (final X509TrustManager trustManager : trustManagers) {
                final List<X509Certificate> list = Arrays.asList(trustManager.getAcceptedIssuers());
                certificates.addAll(list);
            }
            return certificates.toArray(new X509Certificate[] {});
        }
    }
}