package kz.arta.synergy.workflow.mobile.android.authorization;

import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import kz.arta.synergy.workflow.mobile.android.common.requests.SendGetRequest;

/**
 * Created by neox on 28.11.14.
 * Получение сертификата
 */
public class GetCertificate {

    /**
     * Метод получает сертификат с сервера
     *
     * @param url путь до сервера
     * @return сертификат
     */
    public Certificate getCert(String url) {
        try {
            String[] c = url.split(":");
            String host = c[0];
            int port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null);
            SSLContext context = SSLContext.getInstance("TLS");
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(ks);
            X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
            SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
            context.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory factory = context.getSocketFactory();
            SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
            socket.setSoTimeout(SendGetRequest.TIMEOUT);
            try {
                socket.startHandshake();
                socket.close();
            } catch (SSLException ignored) {
            }
            Certificate[] chain = tm.chain;
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            for (Certificate cert : chain) {
                sha1.update(cert.getEncoded());
                md5.update(cert.getEncoded());
            }
            return chain[0];
        } catch (Exception exc) {
            return null;
        }
    }

    /**
     * Метод сохраняет сертификат в локальном хранилище сертификатов
     *
     * @param certificate сертификат
     * @return
     */
    public SSLSocketFactory save(Certificate certificate) {
        try {
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", certificate);
            TrustManagerFactory mainTMF = null;
            try {
                mainTMF = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                mainTMF.init(keyStore);
            } catch (Exception exc) {
                exc.printStackTrace();
            }
            X509TrustManager mainTrustManager = null;
            for (TrustManager tm : mainTMF.getTrustManagers()) {
                if (tm instanceof X509TrustManager) {
                    mainTrustManager = (X509TrustManager) tm;
                    break;
                }
            }
            final X509TrustManager currentTrustManager = mainTrustManager;
            SSLContext sslContext = null;
            try {
                sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{currentTrustManager}, new SecureRandom());
            } catch (Exception exc) {
                exc.printStackTrace();
            }
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    try {
                        currentTrustManager.checkServerTrusted((X509Certificate[]) session.getPeerCertificates(), session.getCipherSuite());
                        return true;
                    } catch (Exception exc) {
                        exc.printStackTrace();
                    }
                    return false;
                }
            });
            return sslContext.getSocketFactory();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
        return null;
    }

    private class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;

        private X509Certificate[] chain;

        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }

        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            throw new UnsupportedOperationException();
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
    }
}
