package com.nbusy.sdk.titan.jsonrpc.neptulon;

import android.net.SSLCertificateSocketFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;

/**
 * Neptulon connection implementation: https://github.com/neptulon/neptulon
 */
public class ConnImp implements Conn {
    private SSLSocket socket;

    public void connect(String pemEncodedCaCert, String pemEncodedClientCert, byte[] privateKey) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
        SSLCertificateSocketFactory factory;
        try (InputStream caCertStream = new ByteArrayInputStream(pemEncodedCaCert.getBytes());
             InputStream clientCertStream = new ByteArrayInputStream(pemEncodedClientCert.getBytes())) {
            factory = getSocketFactory(caCertStream, clientCertStream, privateKey);
        }

        socket = (SSLSocket) factory.createSocket("localhost", 8081);
    }

    public void close() throws IOException {
        socket.close();
    }

    private SSLCertificateSocketFactory getSocketFactory(InputStream caCertStream, InputStream clientCertStream, byte[] privateKey) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
        SSLCertificateSocketFactory factory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(60 * 1000, null);

        // set CA cert to trust
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate ca = (X509Certificate)cf.generateCertificate(caCertStream);
        KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        caKeyStore.load(null, null);
        caKeyStore.setCertificateEntry(ca.getSubjectX500Principal().getName(), ca);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(caKeyStore);
        factory.setTrustManagers(tmf.getTrustManagers());

        // set client cert
        X509Certificate cl = (X509Certificate)cf.generateCertificate(clientCertStream);
        KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        clientKeyStore.load(null, null);
        clientKeyStore.setKeyEntry(cl.getSubjectX500Principal().getName(), privateKey, new Certificate[]{cl});
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(clientKeyStore, new char[]{});

        // todo: alternative approach if above doesn't work
//        keyStore = KeyStore.getInstance("PKCS12");
//        fis = new FileInputStream(certificateFile);
//        keyStore.load(fis, clientCertPassword.toCharArray());

        return factory;
    }
}

@@ -759,24 +759,79 @@
 ents Conn %7B%0A
+    private final SSLCertificateSocketFactory factory;%0A
     private 
@@ -864,20 +864,15 @@
 lic 
-void connect
+ConnImp
 (Str
@@ -960,16 +960,43 @@
 ception,
+ UnrecoverableKeyException,
  Certifi
@@ -1057,82 +1057,10 @@
 tion
-, UnrecoverableKeyException %7B%0A        SSLCertificateSocketFactory factory;
+ %7B
 %0A   
@@ -1346,24 +1346,77 @@
 ;%0A        %7D%0A
+    %7D%0A%0A    public void connect() throws IOException %7B
 %0A        soc
@@ -2081,32 +2081,33 @@
 X509Certificate)
+ 
 cf.generateCerti
@@ -2600,16 +2600,17 @@
 ificate)
+ 
 cf.gener

move factory creation to constructor