title: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
body: <p>I am receiving an exception error when checking if server is trusted, using X509TrustManager. I followed instructions from Developers: <a href="https://developer.android.com/training/articles/security-ssl.html#UnknownCa" rel="nofollow">https://developer.android.com/training/articles/security-ssl.html#UnknownCa</a></p>
  
  <p>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: <a href="https://certs.godaddy.com/repository/" rel="nofollow">https://certs.godaddy.com/repository/</a>. The exact error I get is the following:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>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.</p>
  
  <pre><code>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);
  </code></pre>
  
  <p>I have a Hostname verifier</p>
  
  <pre><code>/**
   * 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();
                      }
                  }
              }
      };
  }
  </code></pre>
  
  <p>Thanks in advance!</p>
  
  <p>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.</p>
  
  <p>I can circumvent this by always returning true, but is this correct? Should I be returning back the domain of my server instead?</p>
  
  <pre><code>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; 
          }
      };
  }
  </code></pre>