3

I am receiving an exception error when checking if server is trusted, using X509TrustManager. I followed instructions from Developers: https://developer.android.com/training/articles/security-ssl.html#UnknownCa

I am trying to take a certificate that is stored locally within my app, and use that certificate with each Volley API request I make to first confirm that the certificate is trusted (verified) or not. I downloaded the certificate from GoDaddy and added to my project: https://certs.godaddy.com/repository/. The exact error I get is the following:

06-09 15:01:20.509 10599-11180/com.app.debug W/System.err: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:324)
06-09 15:01:20.519 10599-11070/com.app.debug I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:215)
06-09 15:01:20.519 10599-11070/com.app.debug I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.udi.app.framework.net.VolleyClient$3.checkServerTrusted(SourceFile:170)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:117)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:643)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:353)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:212)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.Connection.connect(Connection.java:1322)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:1410)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:466)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:447)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:353)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:468)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:410)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:532)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.volley.toolbox.HurlStack.performRequest(SourceFile:109)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.volley.toolbox.BasicNetwork.performRequest(SourceFile:93)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:     at com.android.volley.NetworkDispatcher.run(SourceFile:105)
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err: Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
06-09 15:01:20.519 10599-11180/com.app.debug W/System.err:  ... 22 more

I have a class called "VolleyClient" that manages all the requests I make via Volley. Here is my implementation. I begin by passing in a HurlStack object with my Volley request queue.

HurlStack hurlStack = new HurlStack() {
                @Override
                protected HttpURLConnection createConnection(URL url) throws IOException {
                    HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url);
                    try {
                        httpsURLConnection.setSSLSocketFactory(getSSLSocketFactory());
                        httpsURLConnection.setHostnameVerifier(getHostnameVerifier());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return httpsURLConnection;
                }
            };

RequestQueue mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext(), hurlStack);

I have a Hostname verifier

/**
 * This is used to verify local host. Let's assume the app is hosted inside of a server
 * machine which has a server certificate in which "Issued to" is "localhost", then
 * this method will verify "localhost". If not, can temporarily return true
 * @return
 */
private HostnameVerifier getHostnameVerifier() {
    return new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            //return true;
            HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            return hv.verify("localhost", session);
        }
    };
}

private SSLSocketFactory getSSLSocketFactory()
        throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {

    InputStream caInput = mContext.getResources().openRawResource(R.raw.gdig2); // this certificate file stored in \app\src\main\res\raw folder path
    // From https://www.washington.edu/itconnect/security/ca/load-der.crt
    //InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
    CertificateFactory cf = null;
    Certificate ca = null;
    try {
        cf = CertificateFactory.getInstance("X.509", "BC");
        ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    } finally {
        caInput.close();
    }
    caInput.close();

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType); //KeyStore keyStore = KeyStore.getInstance("BKS");
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());

    // Create an SSLContext that uses our TrustManager
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, wrappedTrustManagers, null);

    return sslContext.getSocketFactory();
}

private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
    final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
    return new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return originalTrustManager.getAcceptedIssuers();
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    try {
                        originalTrustManager.checkClientTrusted(certs, authType);
                    } catch (CertificateException e) {
                        e.printStackTrace();
                    }
                }

                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    try {
                        originalTrustManager.checkServerTrusted(certs, authType);
                    } catch (CertificateException e) {
                        // THIS IS THE EXCEPTION I GET WITH EACH REQUEST MADE
                        e.printStackTrace();
                    }
                }
            }
    };
}

Thanks in advance!

EDIT: This exception occurs when calls to Google are made, because I am using google maps. This call does not use the verified certificate from my server, and thus causes a trust anchor for certification path not found exception.

I can circumvent this by always returning true, but is this correct? Should I be returning back the domain of my server instead?

private HostnameVerifier getHostnameVerifier() {
    return new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            // instead should I return back hv.verify("https:myserver.com", session);
            return true; 
        }
    };
}
portfoliobuilder
  • 7,556
  • 14
  • 76
  • 136
  • 1
    Please use my new updated answer at http://stackoverflow.com/questions/32673568/does-android-volley-support-ssl/32674422#32674422 – BNK Jun 09 '16 at 23:56
  • 1
    Moreover, if the cert you use in Android is the same as the cert in web service, then you can use "tmf.getTrustManagers" instead of getWrapp... – BNK Jun 10 '16 at 00:00
  • @BNK your answer is very similar to what I have implemented, however, I am still unsure of what to return for hostname. You commented that 'verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames'. Does this mean I should specify my server hostname? – portfoliobuilder Jun 10 '16 at 00:00
  • HostnameVerifier does not cause exception "Trust anchor...", I think. It's used for "java.io.IOException: Hostname 'example.com' was not verified" as in Google doc – BNK Jun 10 '16 at 00:02
  • Please tell me if you use verify(...) instead of "return true", any error in logcat? – BNK Jun 10 '16 at 00:09
  • Return true seems to allow all of my API requests to work without any exceptions. But with verify(...) google maps api would return false and that request would not fire off. – portfoliobuilder Jun 10 '16 at 16:19

1 Answers1

-2

Check system time. Make sure its up to date.