I have a custom httpclient that I created to take in my custom trust store, and use it for all ssl sites that it tries to access. Here's the code for that:
public class MyHttpClient extends DefaultHttpClient {
private Context context;
public MyHttpClient(Context context) {
this.context = context;
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
KeyStore trusted = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(
R.raw.cacerts);
try {
trusted.load(in, "changeit".toCharArray());
}
catch (CertificateException c) {
System.out
.println("There was a certificate exception in myhttpclient!");
}
finally {
in.close();
}
return new SSLSocketFactory(trusted);
}
catch (Exception e) {
throw new AssertionError(e);
}
}
}
And here's the stacktrace it's giving me:
W/System.err(4194): javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
W/System.err(4194): at org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:258)
W/System.err(4194): at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)
W/System.err(4194): at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:381)
W/System.err(4194): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
W/System.err(4194): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
W/System.err(4194): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
W/System.err(4194): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:428)
W/System.err(4194): at org.apache.http.impl.client.AbstractHttpClient$1.executeRequestSending(AbstractHttpClient.java:608)
W/System.err(4194): at org.apache.http.impl.client.naf.redirect.NafRequestExecutorWrapperRedirectionHandler.executeRequestSendingUsual(NafRequestExecutorWrapperRedirectionHandler.java:96)
W/System.err(4194): at org.apache.http.impl.client.naf.redirect.NafRequestExecutorWrapperRedirectionHandler.executeRequestSending(NafRequestExecutorWrapperRedirectionHandler.java:73)
W/System.err(4194): at org.apache.http.impl.client.naf.auth.NafHttpAuthStrategyDefault.sendFirstRequest(NafHttpAuthStrategyDefault.java:487)
W/System.err(4194): at org.apache.http.impl.client.naf.auth.NafHttpAuthStrategyDefault.performAuthExecutionUnsafe(NafHttpAuthStrategyDefault.java:388)
W/System.err(4194): at org.apache.http.impl.client.naf.auth.NafHttpAuthStrategyDefault.performAuthExecution(NafHttpAuthStrategyDefault.java:200)
W/System.err(4194): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:556)
W/System.err(4194): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:505)
W/System.err(4194): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:483)
W/System.err(4194): at com.wmmccreedy.vce.AgConnection.submitInfo(AgConnection.java:111)
W/System.err(4194): at com.wmmccreedy.vce.LoginSubmitActvity$DownloadWebPageTask.doInBackground(LoginSubmitActvity.java:199)
W/System.err(4194): at com.wmmccreedy.vce.LoginSubmitActvity$DownloadWebPageTask.doInBackground(LoginSubmitActvity.java:1)
W/System.err(4194): at android.os.AsyncTask$2.call(AsyncTask.java:185)
W/System.err(4194): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
W/System.err(4194): at java.util.concurrent.FutureTask.run(FutureTask.java:138)
W/System.err(4194): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
W/System.err(4194): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
W/System.err(4194): at java.lang.Thread.run(Thread.java:1019)
Now, this works correctly... about 50% of the time. I "solved" this by creating a while loop. It continues to recreate the httpclient client and try to access the server over and over again until it works, usually after only 1 to 2 attempts (max I've seen is 4). Obviously, this is very inefficient.
I've narrowed down the problem to the class I've posted above, since if I create the httpclient just once and try to access the site using that same class multiple times, it will either always fail, or always succeed, depending on whether I got a 'good' httpclient, or a 'bad' httpclient. However, if I create the httpclient every single time I try and access the webpage, it will sometimes work and sometimes not work.
So why is it doing this, and how can I fix this? And why is it only working intermittently, what could be changing between creations of the client?
Edit: Solved!
It appears that I had left some old versions of some aliases in my truststore, and it was randomly picking whichever one it found first, which didn't always end up being the correct one. Each alias had all the same certs in them, but each had all of the certs in a different order. I tested until I found the correct store, deleted the rest, and everything is perfect now.
This is a problem in Apache HTTP Client (observed on Android 2.3). When you load keystore, the certificates are loaded in memory data structure in random order. The PKIX path verification logic in Apache HTTP Client is faulty. It starts with server's certificate and looks into keystore, if the first certificate that is looked up is CA certificate, then chain is built successfully and verification succeeds, however, if the first certificate that is looked up is server's certificate then Apache HTTP client gives up and verification fails.
Therefore, make sure to not have server's and intermediate CA certificates in your keystore.
This is not a 'certificate creation' problem.
The server (the peer) didn't send you a certificate. This is probably because it couldn't find one in its keystore that was signed by someone trusted by your truststore.