I'm writing an Android App which connects to a Server to call some Webservices. This server uses a self-signed Certificate for SSL and requires a Client Certificate for Authentication.
When I connect to the server with Android Chrome browser or with the safari browser from an Iphone it works perfectly. The SSL connection is establishing and the Client Certificate Authentication is succeding and redirecting to my Webservices. But when I connect with other browsers, like the stock android browser, dolphin etc. it is resulting in an Network-Timeout.
Also when I connect with my Android App I get the following error:
javax.net.ssl.SSLException: Read error: ssl=0x12746b8: I/O error during system call, Connection reset by peer
at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_read(Native Method)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:671)
at libcore.io.Streams.readSingleByte(Streams.java:41)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:655)
at libcore.io.Streams.readAsciiLine(Streams.java:201)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:544)
I followed the blog of chariotsolutions: http://chariotsolutions.com/blog/post/https-with-client-certificates-on/
I also converted the Client Certificate to BKS Format with portecle GUI.
Here is my Code:
private static final String USER_AGENT = "Mozilla/5.0 (Linux; U; Android 4.0.3; et-ee; GT-P5100 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30";
....
@Override
protected Object doInBackground(String... urlString) {
....
URL url = new URL(urlString[0]);
HttpsURLConnection urlConnection =(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(getSSLContext().getSocketFactory());
urlConnection.addRequestProperty("User-Agent", USER_AGENT);
urlConnection.setDoInput(true);
urlConnection.connect();
....
}
private SSLContext getSSLContext() throws CertificateException,IOException, KeyStoreException, NoSuchAlgorithmException,KeyManagementException, java.security.cert.CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caRootInput = context.getAssets().open("MySSLCertificate_here.crt");
InputStream caClientAuth = context.getAssets().open("MyCientCertificate_here");
....
Certificate caRoot;
caRoot = cf.generateCertificate(caRootInput);
// KeyStore and KeyManager for Client Certificate
KeyStore keystore_client = KeyStore.getInstance("BKS");
keystore_client.load(caClientAuth, "My_passWord_here".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore_client, "My_passWord_here".toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
// KeyStore and Trustmanager for SSL Certificate
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("caRoot", caRoot);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(keyManagers, tmf.getTrustManagers(), null);
return context;
....
}
Notice: I also have an option of connecting to the server without Client Certificate Authentication (resulting in an different URL) for testing purposes. The Code above in this case is the same but with only context.init(null, tmf.getTrustManagers(), null). This is working perfectly, establishing an SSL Connection and redirecting me to my Webservices.
What really bothers me is that everything is working with Chrome and Safari on Iphone. So I guess the problem might only be in my Android Code?
Does anyone had an similar Problem and has an Idea or maybe an Solution to his?
Thank you very much!
EDIT:
I forgot to mention, that I also set up a Cookie Manager, which was needed for the SSL Connection in the first place. (Set up in the MainActivity in onCreate())
// Required for SSL connection
CookieHandler.setDefault(new CookieManager());
Without that, i was getting the same error on trying to establish an SSL Connection (without Client Certificate), but sadly it's not helping with Client Cert too.
UPDATE #2
With much Backgroundinformation from nelenkov.blogspot.de/2011/12/using-custom-certificate-trust-store-on.html (Thanks for this gread Article @ Nikolay) and his Android Connectiontest Application on Github i found some interesting things.
The Server is set up to give me a Cookie when connecting. That for the Conncetion "works" if I connect without Client Authentication in an prior Step, so that on the second connect (with Client Auth) he only verifys that Cookie and let me pass.
Second thing I finally noticed, the Server gives me a Nonce. I guess the Connection should follow that Redirect automatically by returning that Nonce. Some Browsers, like Firefox and the stock Browser are stopping the connection, (after getting redirected with Responsecode 302) and finally stopping with 404 resulting an URL like ..policy?nonce=fno3i9Hsfn3uz. Then I have to manually reload this URL to get through to the Webservices. Sadly this doesn't works with my Android Application.
I guess, this problem might be an misconfiguration on the serverside then?