package com.fastcashier.lib_common.net

import java.io.IOException
import java.io.InputStream
import java.security.*
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.*

object SSLCertificatesInit {
    const val TLS = "TLS"
    const val SSL = "SSL"
    const val SSLV2 = "SSLv2"
    private const val SERVER_CERTIFICATE_TYPE = "X.509"
    private const val CLIENT_CERTIFICATE_TYPE = "BKS"

    /**
     * 信任所有站点
     *
     * @param socketFactory 输出参数，在外部创建1个SSLSocketFactory数组
     * @param trustManager  输出参数，在外部创建1个X509TrustManager数组
     */
    fun init(socketFactory: Array<SSLSocketFactory?>, trustManager: Array<X509TrustManager?>) {
        try {
            val sslContext = SSLContext.getInstance(TLS)
            trustManager[0] = object : X509TrustManager {
                override fun getAcceptedIssuers(): Array<X509Certificate> {
                    return arrayOf()
                }

                override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
                override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
            }
            sslContext.init(null, arrayOf<TrustManager?>(trustManager[0]), SecureRandom())
            socketFactory[0] = sslContext.socketFactory
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: KeyManagementException) {
            e.printStackTrace()
        }
    }

    /**
     * 单向验证
     *
     * @param socketFactory 输出参数，在外部创建1个SSLSocketFactory数组
     * @param trustManager  输出参数，在外部创建1个X509TrustManager数组
     * @param in            服务器cer证书文件的InputStream对象
     */
    fun init(
        socketFactory: Array<SSLSocketFactory?>,
        trustManager: Array<X509TrustManager?>,
        `in`: InputStream
    ) {
        try {
            val sslContext = SSLContext.getInstance(TLS)
            val tmArr = createTrustManagers(genServerKeyStore(`in`))
            sslContext.init(null, tmArr, SecureRandom())
            trustManager[0] = getX509TrustManager(tmArr)
            socketFactory[0] = sslContext.socketFactory
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: KeyManagementException) {
            e.printStackTrace()
        } catch (e: KeyStoreException) {
            e.printStackTrace()
        } catch (e: CertificateException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            try {
                `in`.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    @Throws(KeyStoreException::class, NoSuchAlgorithmException::class)
    private fun createTrustManagers(ks: KeyStore): Array<TrustManager> {
        val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
        tmf.init(ks)
        return tmf.trustManagers
    }

    @Throws(
        KeyStoreException::class,
        CertificateException::class,
        NoSuchAlgorithmException::class,
        IOException::class
    )
    private fun genServerKeyStore(`in`: InputStream): KeyStore {
        val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
        keyStore.load(null)
        val cf = CertificateFactory.getInstance(SERVER_CERTIFICATE_TYPE)
        keyStore.setCertificateEntry(SERVER_CERTIFICATE_TYPE, cf.generateCertificate(`in`))
        return keyStore
    }

    /**
     * 双向验证
     *
     * @param socketFactory 输出参数，在外部创建1个SSLSocketFactory数组
     * @param trustManager  输出参数，在外部创建1个X509TrustManager数组
     * @param serverIn      服务器cer证书文件的InputStream对象
     * @param clientIn      客户端bks证书的InputStream对象
     * @param clientPwd     客户端bks证书的密码
     */
    fun getSocketFactory(
        socketFactory: Array<SSLSocketFactory?>,
        trustManager: Array<X509TrustManager?>,
        serverIn: InputStream,
        clientIn: InputStream,
        clientPwd: String
    ) {
        try {
            val sslContext = SSLContext.getInstance(TLS)
            val tmArr = createTrustManagers(genServerKeyStore(serverIn))
            sslContext.init(
                createKeyManagers(genClientKeyStore(clientIn, clientPwd), clientPwd),
                tmArr,
                SecureRandom()
            )
            trustManager[0] = getX509TrustManager(tmArr)
            socketFactory[0] = sslContext.socketFactory
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: KeyManagementException) {
            e.printStackTrace()
        } catch (e: KeyStoreException) {
            e.printStackTrace()
        } catch (e: CertificateException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: UnrecoverableKeyException) {
            e.printStackTrace()
        } finally {
            try {
                serverIn.close()
                clientIn.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    @Throws(
        KeyStoreException::class,
        NoSuchAlgorithmException::class,
        UnrecoverableKeyException::class
    )
    private fun createKeyManagers(ks: KeyStore, pwd: String): Array<KeyManager> {
        val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
        kmf.init(ks, pwd.toCharArray())
        return kmf.keyManagers
    }

    @Throws(
        KeyStoreException::class,
        CertificateException::class,
        NoSuchAlgorithmException::class,
        IOException::class
    )
    private fun genClientKeyStore(`in`: InputStream, pwd: String): KeyStore {
        val ks = KeyStore.getInstance(CLIENT_CERTIFICATE_TYPE)
        ks.load(`in`, pwd.toCharArray())
        return ks
    }

    private fun getX509TrustManager(tmArr: Array<TrustManager>): X509TrustManager? {
        for (tm in tmArr) {
            if (tm is X509TrustManager) {
                return tm
            }
        }
        return null
    }
}