4

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.

Michael
  • 3,334
  • 20
  • 27
  • 1
    What happens if you just retry the request "N" times in a row without recreating the client? – Jens May 27 '12 at 08:14
  • It either continues to not work, or continues to work. Whatever it does, it continues to do it. – Michael May 27 '12 at 08:42
  • 1
    Hm, if you dump the aliases in the malfunctioning `KeyStore` and compare them to a functioning instance? Same order? Missing aliases? – Jens May 27 '12 at 08:44
  • I'm going to try that next, but it shouldn't make a difference. The trust store is saved in the project directory, and never changes between uses, so the order should never change. An exception is never thrown when the store is getting added to android's either... – Michael May 27 '12 at 08:47
  • 1
    Well, failing to properly load the `KeyStore` sounds like the one culprit that's left - since the failures are consistent when retrying with a broken store. – Jens May 27 '12 at 08:48
  • Alright, so it appears that both functioning and non functioning instances print all of the same aliases, even in the same order. – Michael May 27 '12 at 09:26
  • I figured it out. It appears that the issue was that I had multiple certificate chains, with the same certificates in them, but in different orders. I deleted the other four of them and it appears to be working now! I'm new here, how do I give you credit for helping me out? Since you didn't submit an answer I can't mark you as helping. Or do I just submit my own answer? – Michael May 27 '12 at 09:36
  • 1
    Since you figured it out yourself you should submit an answer or update your question with the solution. – Jens May 27 '12 at 09:57

2 Answers2

2

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 2
    If this was the server's problem, wouldn't continuing to try with the same client eventually work? I'm always using the same trust store, nothing changes, so it should always find the same certificate, and the server should always return the same certificate. Correct? – Michael May 27 '12 at 08:44
  • @Michael No it wouldn't work. The client tells the server what CAs it trusts. If the client's truststore is deficient in this regard why would what the server does ever change. – user207421 May 27 '12 at 09:31
  • @downvoter Please explain your downvote. Unexplained downvotes don't help anybody: not me, not the OP, not the future readers of the thread; and they also raise other suspicions. In this case it raises the suspicion with me that you don't know what you're talking about. – user207421 May 27 '12 at 09:32
  • Well, I figured it out thanks to yours and @Jens 's suggestion. 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 had 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. I'm going to accept your answer since, although you didn't lead me to the correct answer directly, you were still technically correct about what was going on. – Michael May 27 '12 at 10:14
  • While you're absolutely correct to say that "`No peer certificate`" is normally caused by the server not sending a certificate, I've noticed that some versions of Apache HTTP Client "swallow" the PKIX path exception when a non-trusted cert is used, and carry on a few steps to throw "`No peer certificate`" then. It seems due to the unusual way Apache HTTP Client is implemented around this aspect. – Bruno Mar 04 '14 at 14:49
0

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.

FaST4
  • 259
  • 2
  • 8