Getting the \"javax.net.ssl.SSLPeerUnverifiedException: No peer certificate error\"
in an emulator running Android 2.3 but NOT in 4. In 4 it works perfectly. I\
Another source of this message can be an invalid date/time setting, e.g. when using a device which has gone a few months without power. Quite trivial, but it can be hard to spot.
Certificate verification (or more precisely - chain building) logic in (at least) Android 2.3 is faulty.
Here is what I have observed:
If the TLS server in it's certificate chain provides only server's certificate (non-selfsigned or selfsigned), then you can put server's certificate in keystore and verification will succeed.
If the TLS server in it's certificate chain provides also intermediate CA certificate, then in the keystore you must put only root CA certificate and make sure that the keystore does NOT contain server's and intermediate CA certificates (otherwise the verification will fail randomly).
If the TLS server in it's certificate chain provides intermediate and root CA certificates in the correct order, then you just have to make sure that root CA certificate is in the keystore (doesn't matter if server's and intermediate CA certificates are there).
So the "correct/reliable" way how to handle this is to include in the keystore only root CA certificates and blame server configuration for "No peer certificate" - in case server's certificate chain does not provide intermediate CA certificates or certificates are in incorrect order. You can test server using https://www.ssllabs.com/ssltest/.
I had exactly the same issue as you. Everything was working fine with android >3.X but when I tried with some (but not all !) 2.3.X devices I got that famous "No peer certificate error" exception.
I dug a lot through stackoverflow and other blogs but I haven't found anything that worked on those "rogue" devices (in my case: correct use of truststore; no sni required; correct cert chain order on server; etc ...).
It's look like that android's Apache HttpClient was just not working correctly on some 2.3.X devices. The "no peer certificates" exception was occurring too early to even reach a custom hostname verifier code, so solution like that one were not working for me.
Here was my code :
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream is = this.getAssets().open("discretio.bks");
trustStore.load(is, "discretio".toCharArray());
is.close();
SSLSocketFactory sockfacto = new SSLSocketFactory(trustStore);
sockfacto.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", sockfacto, 443));
SingleClientConnManager mgr = new SingleClientConnManager(httpParameters, schemeRegistry);
HttpClient client = new DefaultHttpClient(mgr, httpParameters);
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
So I rewrote everything using javax.net.ssl.HttpsURLConnection and now it's working on all devices I have tested (from 2.3.3 to 4.X).
Here is my new code :
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream is = this.getAssets().open("discretio.bks");
trustStore.load(is, "discretio".toCharArray());
is.close();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
URL request = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) request.openConnection();
//ensure that we are using a StrictHostnameVerifier
urlConnection.setHostnameVerifier(new StrictHostnameVerifier());
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setConnectTimeout(15000);
InputStream in = urlConnection.getInputStream();
//I don't want to change my function's return type (laziness) so I'm building an HttpResponse
BasicHttpEntity res = new BasicHttpEntity();
res.setContent(in);
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, urlConnection.getResponseCode(), "");
resp.setEntity(res);
Certificate chain and hostname validation are working (I tested them). If anyone want to take a better look on the change, here is a diff
Comments are welcome, I hope it will help some people.
This thread was really helpful when I debugged a similar issue.
Summary Android 2.3 HTTPS/SSL checklist:
What certificates are you loading from R.raw.my_cert
? This error either speaks to having a misconfigured server -- not installing Thawte's primary and secondary intermediate CAs -- or you not loading and trusting the correct certificate chain.