Security especially SSL Sockets is a Pain in the Ass in Android.
In Java you can create an “inner” SSL Socket over an “outer” SSL Socket with the following code snippet:
SSLSocketFactory SSLSocketFactory = (SSLSocketFactory) OuterSSLContext.getSocketFactory(); SSLSocket innerSSLSocket = (SSLSocket) SSLSocketFactory.createSocket(socket, socket.getInetAddress() .getHostAddress(), socket.getPort(), true);
But in Android this code snippet will throw a SSL-Handshake Exception:
W/System.err( 433): javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x2672b0: Failure in SSL library, usually a protocol error W/System.err( 433): error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol (external/openssl/ssl/s23_srvr.c:589 0xad12959f:0x00000000)
Another “solution” is changing the SSL-Provider.Normally, Android uses the OpenSSL-Provider.
http://stackoverflow.com/questions/13482224/sslsocket-over-another-sslsocket
Switching the Provider to org.apache.harmony.xnet.provider.jsse throws an Nullpointer Exception for my case.
Here is my code snippet using org.spongycastle that creates an inner Socket successfully. Sadly some Methods are deprecated – but I was unable to find another solution.
package com.example.tlsexample; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.security.KeyStore; import java.security.Provider; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; 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 org.spongycastle.asn1.x509.X509CertificateStructure; import org.spongycastle.crypto.tls.AlwaysValidVerifyer; import org.spongycastle.crypto.tls.CertificateVerifyer; import org.spongycastle.crypto.tls.DefaultTlsClient; import org.spongycastle.crypto.tls.PSKTlsClient; import org.spongycastle.crypto.tls.TlsAuthentication; import org.spongycastle.crypto.tls.TlsPSKIdentity; import org.spongycastle.crypto.tls.TlsProtocolHandler; import android.content.Context; import android.os.AsyncTask; import android.util.Log; public class Socket { private Context context; private String serverURL = "10.25.0.27"; private String serverPort = "61111"; public Socket(Context context) { super(); this.context = context; doInBackground(); } protected void doInBackground() { SSLContext outerSSLContext = null; try { /* * SETUP TRUSTSTORE */ KeyStore trustStore = KeyStore.getInstance("BKS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); InputStream trustStoreStream = context.getResources() .openRawResource(R.raw.ca); trustStore.load(trustStoreStream, "000000".toCharArray()); trustManagerFactory.init(trustStore); /* * SETUP KEYSTORE */ KeyStore keyStore = KeyStore.getInstance("PKCS12"); KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); InputStream keyStoreStream = context.getResources() .openRawResource(R.raw.client); keyStore.load(keyStoreStream, "000000".toCharArray()); keyManagerFactory.init(keyStore, "000000".toCharArray()); /* * SETUP the Outer-SSL context to use the truststore and keystore */ outerSSLContext = SSLContext.getInstance("TLS"); outerSSLContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); /* * SETUP + CONNECT SSLSocketFactory for outerSSLContext */ SSLSocketFactory outerSSLSocketFactory = (SSLSocketFactory) outerSSLContext .getSocketFactory(); SSLSocket outerSocket = (SSLSocket) outerSSLSocketFactory .createSocket(serverURL, Integer.parseInt(serverPort)); /* * StartHandshake outerSocket */ outerSocket.startHandshake(); /* * CONFIGURE: attach a TLSHandler to the sockets out/instream */ TlsProtocolHandler handler = new TlsProtocolHandler( outerSocket.getInputStream(), outerSocket.getOutputStream()); // Connect the handler, ignore server certificates handler.connect(new AlwaysValidVerifyer()); OutputStream os = handler.getOutputStream(); InputStream is = handler.getInputStream(); System.out.println("finished..."); //quick and dirty Exception Handling } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
The post Android: Create SSL-Socket over SSL-Socket with Client Authentication appeared first on Just another Blog!.