package de.geeksfactory.opacclient.networking;

//Based on
//http://stackoverflow.com/a/6378872/336784
//and
//http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/SSERefGuide.html#X509TrustManager
//and
//https://github.com/nelenkov/custom-cert-https

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;

import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

/**
 * Allows you to trust certificates from additional KeyStores in addition to the default KeyStore
 */
public class AdditionalKeyStoresSSLSocketFactory {

    /**
     * Creates a customized keystore
     *
     * @param keyStore      The keystore object that should be used in addition to the environments
     *                      default key store.
     * @param socketFactory The class that should be used to instantiate a new socket factory, must
     *                      be a subclass of {@link SSLConnectionSocketFactory}.
     * @return a new {@link SSLConnectionSocketFactory}
     */
    public static SSLConnectionSocketFactory create(KeyStore keyStore, Class<?> socketFactory)
            throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)},
                null);
        if (socketFactory != null) {
            try {
                return (SSLConnectionSocketFactory) socketFactory
                        .getDeclaredConstructor(SSLContext.class).newInstance
                                (sslContext);
            } catch (Exception e) {
                // Fall back to default
                e.printStackTrace();
            }
        }
        return new SSLConnectionSocketFactory(sslContext);
    }

    public static class AdditionalKeyStoresTrustManager implements
            X509TrustManager {

        private X509TrustManager defaultTrustManager;
        private X509TrustManager localTrustManager;
        private X509Certificate[] acceptedIssuers;

        public AdditionalKeyStoresTrustManager(KeyStore localKeyStore) {
            try {
                TrustManagerFactory tmf = TrustManagerFactory
                        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
                tmf.init((KeyStore) null);

                defaultTrustManager = findX509TrustManager(tmf);
                if (defaultTrustManager == null) {
                    throw new IllegalStateException(
                            "Couldn't find X509TrustManager");
                }

                localTrustManager = new LocalStoreX509TrustManager(
                        localKeyStore);

                List<X509Certificate> allIssuers = new ArrayList<>();
                Collections.addAll(allIssuers, defaultTrustManager
                        .getAcceptedIssuers());
                Collections.addAll(allIssuers, localTrustManager
                        .getAcceptedIssuers());
                acceptedIssuers = allIssuers
                        .toArray(new X509Certificate[allIssuers.size()]);
            } catch (GeneralSecurityException e) {
                throw new RuntimeException(e);
            }

        }

        static X509TrustManager findX509TrustManager(TrustManagerFactory tmf) {
            TrustManager tms[] = tmf.getTrustManagers();
            for (TrustManager tm : tms) {
                if (tm instanceof X509TrustManager) {
                    return (X509TrustManager) tm;
                }
            }

            return null;
        }

        public static String getThumbPrint(X509Certificate cert)
                throws NoSuchAlgorithmException, CertificateEncodingException {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] der = cert.getEncoded();
            md.update(der);
            byte[] digest = md.digest();
            return hexify(digest);

        }

        public static String hexify(byte bytes[]) {
            char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
                    '9', 'a', 'b', 'c', 'd', 'e', 'f'};

            StringBuilder buf = new StringBuilder(bytes.length * 2);

            for (byte aByte : bytes) {
                buf.append(hexDigits[(aByte & 0xf0) >> 4]);
                buf.append(hexDigits[aByte & 0x0f]);
            }

            return buf.toString();
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            try {
                defaultTrustManager.checkClientTrusted(chain, authType);
            } catch (CertificateException ce) {
                localTrustManager.checkClientTrusted(
                        new X509Certificate[]{chain[0]}, authType);
            }
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            try {
                defaultTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException ce) {
                localTrustManager.checkServerTrusted(
                        new X509Certificate[]{chain[0]}, authType);
            }
        }

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

        static class LocalStoreX509TrustManager implements X509TrustManager {

            private X509TrustManager trustManager;

            LocalStoreX509TrustManager(KeyStore localTrustStore) {
                try {
                    TrustManagerFactory tmf = TrustManagerFactory
                            .getInstance(TrustManagerFactory
                                    .getDefaultAlgorithm());
                    tmf.init(localTrustStore);

                    trustManager = findX509TrustManager(tmf);
                    if (trustManager == null) {
                        throw new IllegalStateException(
                                "Couldn't find X509TrustManager");
                    }
                } catch (GeneralSecurityException e) {
                    throw new RuntimeException(e);
                }

            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                trustManager.checkClientTrusted(chain, authType);
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                trustManager.checkServerTrusted(chain, authType);
            }

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

}
@@ -5272,64 +5272,13 @@
 ted(
-%0A                        new X509Certificate%5B%5D%7B
 chain
-%5B0%5D%7D
 , au
@@ -5652,64 +5652,13 @@
 ted(
-%0A                        new X509Certificate%5B%5D%7B
 chain
-%5B0%5D%7D
 , au

allow to put root into keystore instead of server certificate