com.archivas.clienttools.arcutils.utils.net.GetCertsX509TrustManager.java Source code

Java tutorial

Introduction

Here is the source code for com.archivas.clienttools.arcutils.utils.net.GetCertsX509TrustManager.java

Source

// Copyright 2007 Hitachi Data Systems
// All Rights Reserved.
//
// Licensed 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
//
// 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 com.archivas.clienttools.arcutils.utils.net;

import com.archivas.clienttools.arcutils.config.ConfigurationHelper;
import com.archivas.clienttools.arcutils.profile.AbstractProfileBase;

import com.archivas.clienttools.arcutils.profile.HCAPProfile;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;

import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Reference Material:
 * http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#TrustManager
 * http://hc.apache.org/httpcomponents-client/tutorial/html/connmgmt.html#d4e497
 * <p>
 * EasyX509TrustManager unlike default {@link javax.net.ssl.X509TrustManager} accepts self-signed
 * certificates.
 * </p>
 */

public class GetCertsX509TrustManager implements X509TrustManager {
    public static final String PACKAGE_NAME = GetCertsX509TrustManager.class.getPackage().getName();
    public static final String CLASS_FULL_NAME = GetCertsX509TrustManager.class.getName();
    public static final String CLASS_NAME = CLASS_FULL_NAME.substring(PACKAGE_NAME.length() + 1);
    public static Logger LOG = Logger.getLogger(CLASS_FULL_NAME);

    private static File persistedKeystoreFile = null;
    private static char[] persistedKeystorePassword = "password".toCharArray();

    private X509TrustManager standardTrustManager = null;
    private static X509TrustManager persistedTrustManager = null;
    private static KeyStore persistedKeyStore = null;

    private static X509TrustManager memoryTrustManager = null;
    private static KeyStore memoryKeyStore = null;

    private GetCertsCallback callback = null;
    private SSLCertificateCallback sslExceptionCallback = null;
    private String hostname = null;
    private AbstractProfileBase profile = null;

    private static final Object LOCK = new Object();

    public GetCertsX509TrustManager(HCAPProfile profile, final SSLCertificateCallback sslExceptionCallback)
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException {
        super();
        this.profile = profile;
        this.hostname = profile.getHostname(); // getHostname returns the hostname provided in the
                                               // profile wizard

        synchronized (LOCK) {
            this.sslExceptionCallback = sslExceptionCallback;
            LOG.log(Level.FINER, "Entering GetCertsX509TrustManager(Keystore, callback)");
            initStandardTrustManager(null);
            initPersistedTrustManager();
            initMemoryTrustManager();

            if (standardTrustManager == null && persistedTrustManager == null && memoryTrustManager == null) {
                throw new NoSuchAlgorithmException("no trust manager found");
            }
        }
    }

    public void initStandardTrustManager(KeyStore keystore)
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException {
        TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        factory.init(keystore);
        TrustManager[] trustmanagers = factory.getTrustManagers();

        // Iterate over the returned trustmanagers, look for an instance of X509TrustManager.
        // If found, use that as our "default" trust manager.
        for (int i = 0; i < trustmanagers.length; i++) {
            if (trustmanagers[i] instanceof X509TrustManager) {
                if (standardTrustManager == null) {
                    standardTrustManager = (X509TrustManager) trustmanagers[i];
                }
                // break;
                LOG.log(Level.FINER, "standardTrustManager=" + trustmanagers[i]);
            }
        }
    }

    public void initPersistedTrustManager()
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException {
        initPersistedTrustManager(false);
    }

    public void initPersistedTrustManager(boolean forcereload)
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException {
        if (persistedTrustManager != null && !forcereload) {
            return;
        }

        String homedir = System.getProperty("user.home");
        String fileNameTemplate = ConfigurationHelper.USER_CONFIG_DIRECTORY
                + ConfigurationHelper.getStringProperty("ssl.keystore.filename", "cacerts");
        String fileName = MessageFormat.format(fileNameTemplate, homedir);
        persistedKeystoreFile = new File(fileName);

        try {
            persistedKeyStore = KeyStore.getInstance("JKS");
            try {
                FileInputStream fis = null;
                if (persistedKeystoreFile.exists()) {
                    fis = new FileInputStream(persistedKeystoreFile);
                }
                persistedKeyStore.load(fis, persistedKeystorePassword);
            } catch (FileNotFoundException e) {
                // Don't Care. Go on.
                LOG.log(Level.WARNING, "Unexpected Exception", e);
            } catch (IOException e) {
                LOG.log(Level.WARNING, "Unexpected Exception", e);
            } catch (CertificateException e) {
                LOG.log(Level.WARNING, "Unexpected Exception", e);
            }

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(persistedKeyStore);

            TrustManager tms[] = tmf.getTrustManagers();

            // Iterate over the returned trustmanagers, look for an instance of X509TrustManager.
            // If found, use that as our "default" trust manager.
            for (int i = 0; i < tms.length; i++) {
                if (tms[i] instanceof X509TrustManager) {
                    persistedTrustManager = (X509TrustManager) tms[i];
                    break;
                }
            }
            LOG.log(Level.FINER, "persistedTrustManager=" + persistedTrustManager);
        } catch (KeyStoreException e) {
            LOG.log(Level.WARNING, "Unexpected Exception", e);
            throw e;

        } catch (NoSuchAlgorithmException e) {
            LOG.log(Level.WARNING, "Unexpected Exception", e);
            throw e;
        } catch (RuntimeException e) {
            LOG.log(Level.WARNING, "Unexpected Exception", e);
            throw e;
        }
    }

    public void initMemoryTrustManager()
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException {
        initMemoryTrustManager(false);
    }

    public void initMemoryTrustManager(boolean forcereload)
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException {
        if (memoryTrustManager != null && !forcereload) {
            return;
        }
        try {
            if (memoryKeyStore == null) {
                memoryKeyStore = KeyStore.getInstance("JKS");
            }

            try {
                memoryKeyStore.load(null, persistedKeystorePassword);
            } catch (IOException e) {
                LOG.log(Level.WARNING, "Unexpected Exception", e);
            } catch (CertificateException e) {
                LOG.log(Level.WARNING, "Unexpected Exception", e);
            }

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(memoryKeyStore);

            TrustManager tms[] = tmf.getTrustManagers();

            // Iterate over the returned trustmanagers, look for an instance of X509TrustManager.
            // If found, use that as our "default" trust manager.
            for (int i = 0; i < tms.length; i++) {
                if (tms[i] instanceof X509TrustManager) {
                    memoryTrustManager = (X509TrustManager) tms[i];
                    break;
                }
            }
            LOG.log(Level.FINER, "MemoryTrustManager=" + memoryTrustManager);
        } catch (KeyStoreException e) {
            LOG.log(Level.WARNING, "Unexpected Exception", e);
            throw e;

        } catch (NoSuchAlgorithmException e) {
            LOG.log(Level.WARNING, "Unexpected Exception", e);
            throw e;

        } catch (RuntimeException e) {
            LOG.log(Level.WARNING, "Unexpected Exception", e);
            throw e;
        }
    }

    /**
     * @see javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.X509Certificate[],String
     *      authType)
     */
    public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
        SSLCertChain certChain = new SSLCertChain(authType, certificates);
        synchronized (LOCK) {
            if (callback != null) {
                callback.callback(certChain);
            }

            try {
                standardTrustManager.checkClientTrusted(certificates, authType);
            } catch (CertificateException certException) {
                // do any special handling here, or rethrow exception.
                if (callback != null) {
                    callback.callback(new SSLCertChain(authType, certificates, certException));
                }
                LOG.log(Level.WARNING, "Error checking Client Trust", certException);
                throw certException;
            } catch (RuntimeException certException) {
                // do any special handling here, or rethrow exception.
                LOG.log(Level.WARNING, "Error checking Client Trust", certException);
                throw certException;
            }
        }
    }

    /**
     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[],String
     *      authType)
     */
    public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
        synchronized (LOCK) {
            try {
                if ((certificates != null) && LOG.isLoggable(Level.FINER)) {
                    LOG.log(Level.FINER, "Server certificate chain:");
                    for (int i = 0; i < certificates.length; i++) {
                        LOG.log(Level.FINER, "X509Certificate[" + i + "]=" + certificates[i]);
                    }
                }

                checkServerTrusted1(certificates, authType);
            } catch (CertificateException e) {
                LOG.log(Level.FINEST, "Exception checking trust for certificate.", e);
                throw e;
            } catch (RuntimeException e) {
                LOG.log(Level.WARNING, "Unexpected exception checking trust for certificate.", e);
                throw e;
            }
        }
    }

    public String testHostname(String hostname, SSLCertChain certChain) {
        String result = null;
        String testingCN = null;
        try {
            List<X509Certificate> certList = certChain.getCertificateList();
            String[] cnList = new String[certList.size()];
            Iterator<X509Certificate> i = certList.iterator();

            for (int count = 0; i.hasNext(); ++count) {
                String dn = ((X509Certificate) i.next()).getSubjectDN().getName();
                int cnIndex = dn.indexOf("CN=") + 3;
                if (cnIndex < 0) {
                    LOG.log(Level.FINE, "Hostname not found in certificate " + dn);
                    continue;
                }
                int cnEndIndex = dn.indexOf(',', cnIndex);
                String cn = (cnEndIndex < 0 ? dn.substring(cnIndex + 3) : dn.substring(cnIndex + 3, cnEndIndex));

                // Also remove the *.
                if (cn.startsWith("*.")) {
                    cn = cn.substring(2);
                }

                cnList[count] = cn;

                // I think it is unlikely there are ever multiple certs coming in here.
                testingCN = cn;
            }
            BrowserCompatHostnameVerifier verifier = new BrowserCompatHostnameVerifier();
            verifier.verify(hostname, cnList, null);
        } catch (SSLException e) {
            if (testingCN != null) {
                result = "Host name " + hostname + " is not equal to the certificate issuer's \nhost name "
                        + testingCN;
            }
            LOG.log(Level.FINE, e.getMessage(), e);
        }
        return result;
    }

    public void checkServerTrusted1(X509Certificate[] certificates, String authType) throws CertificateException {
        SSLCertChain certChain = new SSLCertChain(authType, certificates);

        // Try the in-memory, session only trust manager first
        try {
            LOG.log(Level.FINEST, "About to have MemoryTrustManager Test cert: " + certChain);
            memoryTrustManager.checkServerTrusted(certificates, authType);
            LOG.log(Level.FINEST, "MemoryTrustManager Approves of cert: " + certChain);
            if (sslExceptionCallback != null) {
                sslExceptionCallback.validCertCallback(profile, certChain);
            }
            return;
        } catch (Exception certException) {
            // Log it and fall through to the next trust mgr.
            LOG.log(Level.FINE,
                    "Error checking Server Trust via MemoryTrustManager: " + certException.getMessage());
            LOG.log(Level.FINER, "Error checking Server Trust via MemoryTrustManager", certException);
        }

        // Try the persisted to disk trust manager next
        try {
            LOG.log(Level.FINEST, "About to have PersistedTrustManager Test cert: " + certChain);
            persistedTrustManager.checkServerTrusted(certificates, authType);
            LOG.log(Level.FINEST, "PersistedTrustManager Approves of cert: " + certChain);
            if (sslExceptionCallback != null) {
                sslExceptionCallback.validCertCallback(profile, certChain);
            }
            return;
        } catch (Exception certException) {
            // Log it and fall through to the next trust mgr.
            LOG.log(Level.FINE,
                    "Error checking Server Trust via PersistedTrustManager: " + certException.getMessage());
            LOG.log(Level.FINER, "Error checking Server Trust via PersistedTrustManager", certException);
        }

        // Try the default JDK trust manager last. (This has the default JDK root certs + any the
        // user has added)
        try {
            LOG.log(Level.FINEST, "About to have standardTrustManager Test cert: " + certChain);
            standardTrustManager.checkServerTrusted(certificates, authType);
            LOG.log(Level.FINEST, "standardTrustManager Approves of cert: " + certChain);
            /*
             * if ((certificates != null) && (certificates.length == 1)) {
             * certificates[0].checkValidity(); } else {
             * standardTrustManager.checkServerTrusted(certificates,authType); }
             */
            if (sslExceptionCallback != null) {
                sslExceptionCallback.validCertCallback(profile, certChain);
            }
        } catch (CertificateException certException) {
            LOG.log(Level.FINE,
                    "Error checking Server Trust via standardTrustManager: " + certException.getMessage());
            LOG.log(Level.FINER, "Error checking Server Trust via standardTrustManager", certException);
            handleCertFailureCallback(certChain, certException);
        }
    }

    protected void handleCertFailureCallback(SSLCertChain certChain, CertificateException certException)
            throws CertificateException {

        // We need to warn them if the hostname is invalid
        String hostNameWarningString = null;
        if (hostname != null) {
            hostNameWarningString = testHostname(hostname, certChain);
        }

        SSLCertificateCallback.AllowSSLCert allowAccess = SSLCertificateCallback.AllowSSLCert.NO;
        certChain.setCertificateException(certException);
        if (sslExceptionCallback != null) {
            try {
                allowAccess = sslExceptionCallback.exceptionCallback(profile, certChain, hostNameWarningString);
                LOG.log(Level.FINE, "SSLExceptionCallback returned from user: " + allowAccess);
            } catch (Exception e) {
                LOG.log(Level.WARNING,
                        "Unexpected Exception in SSL Certificate Exception Callback.  Disallowing use of certificate: "
                                + certChain,
                        e);
                allowAccess = SSLCertificateCallback.AllowSSLCert.NO;
            }
        }

        if (allowAccess == SSLCertificateCallback.AllowSSLCert.THIS_SESSION_ONLY) {
            // User accepted the certificate. persist it to the keystore.
            try {
                LOG.log(Level.FINER, "About to store certificate in memory keystore: " + certChain);

                memoryKeyStore.setCertificateEntry(certChain.getIssuedByCommonName(),
                        certChain.getCertificateList().get(0));
                initMemoryTrustManager(true);

                LOG.log(Level.FINER, "Successfully stored cert into memory keystore: " + certChain);
            } catch (Exception e) {
                LOG.log(Level.WARNING, "Error saving certificate to memory keystore", e);
            }

        } else if (allowAccess == SSLCertificateCallback.AllowSSLCert.PERMANENTLY_SAVE) {
            // User accepted the certificate. persist it to the keystore.
            try {
                LOG.log(Level.FINER, "About to store certificate in persisted keystore: " + certChain);

                persistedKeyStore.setCertificateEntry(certChain.getIssuedByCommonName(),
                        certChain.getCertificateList().get(0));
                FileOutputStream fos = new FileOutputStream(persistedKeystoreFile);
                persistedKeyStore.store(fos, persistedKeystorePassword);
                initPersistedTrustManager(true);

                LOG.log(Level.FINER, "Successfully stored cert into persisted keystore: " + certChain);
            } catch (Exception e) {
                LOG.log(Level.WARNING, "Error saving certificate to persisted keystore", e);
            }

        } else {
            // If AllowSSLCert.NO or anything else, throw the exception
            throw certException;
        }

        // If we got here, The user accepted the cert and it is stored. Do the callback.
        if (sslExceptionCallback != null) {
            sslExceptionCallback.validCertCallback(profile, certChain);
        }

    }

    /**
     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
     */
    public X509Certificate[] getAcceptedIssuers() {
        return standardTrustManager.getAcceptedIssuers();
    }

}