Home » Android » android – Safely fixing: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

android – Safely fixing: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

Posted by: admin April 23, 2020 Leave a comment

Questions:

There are dozens of posts about this issue (javax.net.ssl.SSLPeerUnverifiedException: No peer certificate) but I haven’t found anything that works for me.

Many posts (like this, and this) “solve” this by allowing all certificates to be accepted but, of course, this is not a good solution for anything other than testing.

Others seem quite localized and don’t work for me. I really hope that someone has some insight that I lack.

So, my problem: I’m testing on a server accessible only through the local network, connecting via HTTPS. Making the call I need to through the browser works fine. No complaining about certificates and if you check the certificates, it all looks good.

When I try on my Android device, I get javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

Here’s the code that calls it:

StringBuilder builder = new StringBuilder();
builder.append( /* stuff goes here*/ );

httpGet.setEntity(new UrlEncodedFormEntity(nameValuePairs));
ResponseHandler<String> responseHandler = new BasicResponseHandler();

// Execute HTTP Post Request. Response body returned as a string
HttpClient httpClient = MyActivity.getHttpClient();
HttpGet httpGet = new HttpGet(builder.toString());

String jsonResponse = httpClient.execute(httpGet, responseHandler); //Line causing the Exception

My code for MyActivity.getHttpClient():

protected synchronized static HttpClient getHttpClient(){
    if (httpClient != null)
        return httpClient;

    HttpParams httpParameters = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(httpParameters, TIMEOUT_CONNECTION);
    HttpConnectionParams.setSoTimeout(httpParameters, TIMEOUT_SOCKET);
    HttpProtocolParams.setVersion(httpParameters, HttpVersion.HTTP_1_1);

    //Thread safe in case various AsyncTasks try to access it concurrently
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
    ClientConnectionManager cm = new ThreadSafeClientConnManager(httpParameters, schemeRegistry);

    httpClient = new DefaultHttpClient(cm, httpParameters);

    CookieStore cookieStore = httpClient.getCookieStore();
    HttpContext localContext = new BasicHttpContext();
    localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);

    return httpClient;
}

Any help would be much appreciated.

Edit

Also just to mention I’ve had other SSL issues with another app but adding the SchemeRegistry portion fixed it for me before.

Edit 2

So far I’ve only tested on Android 3.1, but I need this to work on Android 2.2+ regardless. I just tested on the browser on my Android tab (Android 3.1) and it complains about the certificate. It’s fine on my pc browser, but not on the Android browser or in my app.

Edit 3

Turns out the iOS browser also complains about it. I’m starting to think it’s a certificate chain issue described here (SSL certificate is not trusted – on mobile only)

How to&Answers:

It turns out my code was fine and the problem was that the server was not returning the full certificate chain. For more information see this SO post and this superuser post:

SSL certificate is not trusted – on mobile only

https://superuser.com/questions/347588/how-do-ssl-chains-work

Answer:

Try below code :-

        BasicHttpResponse httpResponse = null;

        HttpPost httppost = new HttpPost(URL);

        HttpParams httpParameters = new BasicHttpParams();
        // Set the timeout in milliseconds until a connection is
        // established.
        int timeoutConnection = ConstantLib.CONNECTION_TIMEOUT;
        HttpConnectionParams.setConnectionTimeout(httpParameters,
                timeoutConnection);
        // Set the default socket timeout (SO_TIMEOUT)
        // in milliseconds which is the timeout for waiting for data.
        int timeoutSocket = ConstantLib.CONNECTION_TIMEOUT;
        HttpConnectionParams
                .setSoTimeout(httpParameters, timeoutSocket);

        DefaultHttpClient httpClient = new DefaultHttpClient(
                httpParameters);
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        nameValuePairs.add(new BasicNameValuePair(ConstantLib.locale,
                locale));

Answer:

In my case everything used to work fine. Suddenly after 2 days my device did not show any https site or image link.

After some investigation it turns out that My time settings was not up to date on device.

I changed my time settings properly and it worked.

Answer:

I had this exception when I used self-signed certificate + ip address. Just add these lines

HostnameVerifier allHostsValid = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

and your HttpURLConnection will work.
That trick is not related to validation against CA! So if I specify wrong CA and use that trick, I will get another exception. So the host remains trusted

Just in case, I will leave code for specifying your own CA here:

String certStr = context.getString(R.string.caApi);
X509Certificate ca = SecurityHelper.readCert(certStr);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("caCert", ca);

tmf.init(ks);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

Do not forget to use cool feature buildTypes and place different CAs for debug/release in res folder.