title: Java Certificate Client SSL: unable to find valid certification path to requested target
body: <p>We require client authentication to send a RESTful request to some of our web services.  I've installed a client cert (.pem) and key on the my local mac os via the key tool.  These are not self signed.</p>
  
  <pre><code>openssl pkcs12 -export -name myservercert -in not_self_signed.crt -inkey server.key -out keystore.p12
  </code></pre>
  
  <p>...and converted to JKS format</p>
  
  <pre><code>keytool -importkeystore -destkeystore mykeystore.jks -srckeystore keystore.p12 -srcstoretype pkcs12 -alias myservercert
  </code></pre>
  
  <p>I'm trying to build a Java client to do the authentication.   Here is what I've come up with so far:</p>
  
  <pre><code>import java.io.FileInputStream;
  import java.security.KeyStore;
  
  import javax.net.ssl.SSLContext;
  
  import org.apache.http.HttpEntity;
  import org.apache.http.client.methods.CloseableHttpResponse;
  import org.apache.http.client.methods.HttpGet;
  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  import org.apache.http.impl.client.CloseableHttpClient;
  import org.apache.http.impl.client.HttpClients;
  import org.apache.http.ssl.SSLContexts;
  import org.apache.http.util.EntityUtils;
  
  
  public class TestClientCustomSSL {
  
      public final static void main(String[] args) throws Exception {
  
          KeyStore keyStore = KeyStore.getInstance("JKS");
          keyStore.load(new FileInputStream("/Users/me/mykeystore.jks"), "mypassword".toCharArray());
  
          SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, "mypassword".toCharArray()).build();
  
          SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                  sslContext,
                  new String[] {"TLSv1"},
                  null,
                  SSLConnectionSocketFactory.getDefaultHostnameVerifier());
  
          CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
  
          try {
  
              HttpGet httpget = new HttpGet("https://restful-service-i-am-calling/v1/endpoint/data?ip=0.0.0.1");
  
              System.out.println("Executing request " + httpget.getRequestLine());
  
              CloseableHttpResponse response = httpclient.execute(httpget);
              try {
                  HttpEntity entity = response.getEntity();
  
                  System.out.println("----------------------------------------");
                  System.out.println(response.getStatusLine());
                  EntityUtils.consume(entity);
              } finally {
                  response.close();
              }
          } finally {
              httpclient.close();
          }
      }
  
  }
  </code></pre>
  
  <p>Below is the stacktrace that I receive.  But based on what I've read <a href="http://code.naishe.in/2011/07/looks-like-article-no-more-unable-to.html" rel="nofollow">here</a> my class should be able to send the request just fine.</p>
  
  <pre><code>Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
      at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
      at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884)
      at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
      at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
      at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1439)
      at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:209)
      at sun.security.ssl.Handshaker.processLoop(Handshaker.java:878)
      at sun.security.ssl.Handshaker.process_record(Handshaker.java:814)
      at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
      at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
      at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
      at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
      at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394)
      at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353)
      at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
      at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
      at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
      at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
      at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
      at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
      at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
      at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
      at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
      at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
      at com.mycompany.main(ClientCustomSSL.java:101)
  Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
      at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
      at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
      at sun.security.validator.Validator.validate(Validator.java:260)
      at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
      at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
      at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
      at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1421)
      ... 20 more
  Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
      at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
      at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
      at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
      ... 26 more
  </code></pre>
  
  <p>Any pointers are appreciated.</p>
  
  <p><strong>EDIT:::</strong></p>
  
  <p>FYI I am able to get a 200 response from the server using the same pem and key that I added to the trust store using wget.</p>
  
  <pre><code>wget --certificate ~/Desktop/my.cert.pem --private-key ~/Desktop/my.key.key https://mycompany.com/v1/939044?data=0.0.0.1
  </code></pre>
  
  <p><strong>EDIT 2:::*</strong></p>
  
  <p>Based on @EJP answer below, also added the cert from the server site:</p>
  
  <pre><code>openssl x509 -in &lt;(openssl s_client -connect the.api.i.am.calling.com:443 -prexit 2&gt;/dev/null) -out ~/Desktop/the.api.i.am.calling.crt
  </code></pre>
  
  <p>...then I imported the cert to the same keystore:</p>
  
  <pre><code>keytool -importcert -file ~/Desktop/the.api.i.am.calling.crt -alias the.api.i.am.calling.com -keystore /Users/me/mykeystore.jks -storepass mypassword
  </code></pre>
  
  <p>Running the list command shows that both certs are in the keystore:</p>
  
  <pre><code>keytool -list -keystore /Users/me/mykeystore.jks 
  Enter keystore password:  *********
  
  Keystore type: JKS
  Keystore provider: SUN
  
  Your keystore contains 2 entries
  
  my.auth.client.cert.com, Oct 17, 2015, PrivateKeyEntry, 
  Certificate fingerprint (SHA1): 3D:95:32:E5:F9:9E:4A:53:84:EB:AB:1B:B9:A2:4C:A5:1B:5E:DA:76
  the.api.i.am.calling.com, Oct 18, 2015, trustedCertEntry, 
  Certificate fingerprint (SHA1): 7C:4A:7B:CE:9B:0B:92:C0:4F:C0:DA:84:CF:F2:24:CF:99:83:0B:3F
  </code></pre>
  
  <p>But am still receiving the same error.</p>
  
  <p><strong>EDIT 3:::</strong></p>
  
  <p>One more thing I forgot to mention.  The only thing I ever gave the server-side team was our client cert names...  Ie something like dev.auth.client.com.  Do I really have to get the server-side cert to store in the keystore?</p>