Guruprasad Rao Guruprasad Rao - 2 months ago 40
Android Question

javax.net.ssl.SSLException: Connection closed by peer android exception - 2 way ssl

Ok. I have been through all the related posts in

SO
as in
Post 1
,
Post 2
, and
Post 3 - No answer
and non
SO
posts relating to this error, but could not get any help from them. I have below
HttpsRequest
to server, which, no matter what, always ends with
Connection closed by peer
exception.

try {
URL e = new URL((URL)null, url);
SSLSocketFactory sockFact = null;
OutputStream outputStream = null;
HttpsURLConnection con = (HttpsURLConnection)e.openConnection();

sockFact = httpsClientRequest.getSSLSocketFactory_Certificate();
con.setSSLSocketFactory(sockFact);
con.setDoOutput(true);
con.setUseCaches(false);
con.setConnectTimeout(60000);
con.setReadTimeout(60000);
con.setRequestMethod("POST");
con.connect();
} catch (Exception var25) {
var25.printStackTrace();
}


and my
getSSLSocketFactory_Certificate
method is as below:

private SSLSocketFactory getSSLSocketFactory_Certificate()
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException {
KeyStore keyStore = null;
KeyManagerFactory keyManagerFactory = null;
String pKeyPassword = null;
File pKeyFile=null;
File tKeyFile=null;
FileInputStream keyInput = null;
KeyStore trustStore = null;
TrustManagerFactory tmf = null;
TrustManager[] tms = null;
SSLContext context = null;
pKeyFile=LoadFile(R.raw.certFile,"certfile.p12");

pKeyPassword = "pKeyPass";
keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load((InputStream)null, pKeyPassword.trim().toCharArray());
keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.trim().toCharArray());
keyInput.close();

keyManagerFactory.init(keyStore, pKeyPassword.trim().toCharArray());
trustStore = KeyStore.getInstance("BKS");
tKeyFile = LoadFile(R.raw.apikstore,"apiks.bks");
trustStore.load(new FileInputStream(tKeyFile), "keyStorePass".trim().toCharArray());
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
tms = tmf.getTrustManagers();
context = SSLContext.getInstance("TLSv1.2");
context.init(keyManagerFactory.getKeyManagers(), tms, new SecureRandom());
System.clearProperty("javax.net.ssl.trustStoreType");
System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStorePassword");
System.clearProperty("javax.net.ssl.keyStore");
System.clearProperty("javax.net.ssl.keyStorePassword");
System.clearProperty("javax.net.ssl.keyStoreType");
return context.getSocketFactory();
}


LoadFile
method

private File LoadFile(int fileID, String FileName){
File file = new File(contxt.getFilesDir() + File.separator + FileName);
try {
InputStream is = contxt.getResources().openRawResource(fileID);
FileOutputStream fileOutputStream = new FileOutputStream(file);

byte buf[]=new byte[1024];
int len;
while((len=is.read(buf))>0) {
fileOutputStream.write(buf,0,len);
}

fileOutputStream.close();
is.close();
} catch (IOException e1) {}
return file;
}


The server works very well as it works flawlessly for same
certificate
and
keyfile
from
iOS
application. We have also prepared
java
project to test this and even that works awesome.

But with android its throwing the exception, when we try to connect with
con.connect()
. Is there anything am doing wrong here. I worked on the above code through various posts online, but it still doesn't go well. Could someone let me know why this is happening?

Answer

The problem was with the TLS version. Our server accepted the request when set to TLS but when set as TLSv1.1 or TLSv1.2 then the problem was with android version. As per the research and from links provided by @Andrew, it is learnt that TLSv1.2 was supported from API20 or later. So we had below work around to get SSLSocketFactory instance. Hope someone might find it useful.

private static class TLSSocketFactory extends SSLSocketFactory{
    private SSLSocketFactory internalSSLSocketFactory;

    public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
        KeyStore keyStore = null;
        KeyManagerFactory keyManagerFactory = null;
        String pKeyPassword = null;
        File pKeyFile=null;
        File tKeyFile=null;
        FileInputStream keyInput = null;
        KeyStore trustStore = null;
        TrustManagerFactory tmf = null;
        TrustManager[] tms = null;
        SSLContext context = null;
        pKeyFile=LoadFile(R.raw.certFile,"certfile.p12");

        pKeyPassword = "pKeyPass";
        keyManagerFactory =  KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load((InputStream)null, pKeyPassword.trim().toCharArray());
        keyInput = new FileInputStream(pKeyFile);
        keyStore.load(keyInput, pKeyPassword.trim().toCharArray());
        keyInput.close();

        keyManagerFactory.init(keyStore, pKeyPassword.trim().toCharArray());
        trustStore = KeyStore.getInstance("BKS");
        tKeyFile = LoadFile(R.raw.apikstore,"apiks.bks");
        trustStore.load(new FileInputStream(tKeyFile), "keyStorePass".trim().toCharArray());
        tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);
        tms = tmf.getTrustManagers();
        context = SSLContext.getInstance("TLSv1.2");
        context.init(keyManagerFactory.getKeyManagers(), tms, new SecureRandom());
        System.clearProperty("javax.net.ssl.trustStoreType");
        System.clearProperty("javax.net.ssl.trustStore");
        System.clearProperty("javax.net.ssl.trustStorePassword");
        System.clearProperty("javax.net.ssl.keyStore");
        System.clearProperty("javax.net.ssl.keyStorePassword");
        System.clearProperty("javax.net.ssl.keyStoreType");
        return context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return internalSSLSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return internalSSLSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
    }

    private Socket enableTLSOnSocket(Socket socket) {
        if(socket != null && (socket instanceof SSLSocket)) {
            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); //This is where you add your required `TLS` versions.
        }
        return socket;
    }
}

and you can just use it as httpsUrlConnection.setSSLSocketFactory(sockFact);

Comments