PKIX path building failed: unable to find valid certification path to requested target
Asked Answered
R

12

50

I am calling some HTTPS web service which the following Client:

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

/**
 * Handles http and https connections. It sends XML request over http (or https)
 * to SOAP web service and receive the XML reply.
 * 
 * @author mhewedy
 * @date 30.10.2010
 */
public class HttpWSXmlClient
{
    private final String ws_url;
    private byte[] requestData;

    public HttpWSXmlClient(String wsUrl)
    {
        this.ws_url = wsUrl;
    }

    public void readRequest(String xmlRequestFilePath)
    {
        try
        {
            InputStream istream = new FileInputStream(xmlRequestFilePath);
            byte[] data = stream2Bytes(istream);
            istream.close();
            this.requestData = data;
        } catch (Exception e)
        {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 
     * @param ps
     *            PrintStream object to send the debugging info to.
     * @return
     * @throws IOException
     */
    public byte[] sendAndRecieve(PrintStream ps) throws IOException
    {
        if (requestData == null)
            throw new RuntimeException(
                    "the request data didn't initialized yet.");
        if (ps != null)
            ps.println("Request:\n" + new String(requestData));
        URL url = new URL(ws_url);
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        // or HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestProperty("content-type", "text/xml");
        connection.connect();
        OutputStream os = connection.getOutputStream();
        os.write(requestData);
        InputStream is = connection.getInputStream();
        byte[] rply = stream2Bytes(is);
        if (ps != null)
            ps.println("Response:\n" + new String(rply));
        os.close();
        is.close();
        connection.disconnect();
        return rply;
    }

    public byte[] sendAndRecieve() throws IOException
    {
        return sendAndRecieve(null);
    }

    private byte[] stream2Bytes(InputStream istream) throws IOException
    {
        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
        int c;
        while ((c = istream.read()) != -1)
        {
            if (c != 0x0A && c != 0x0D) // prevent new line character from being
            // written
            {
                if (c == 0x09)
                    c = 0x20; // prevent tab character from being written,
                // instead write single space char
                outstream.write(c);
            }
        }
        byte[] ret = outstream.toByteArray();
        outstream.close();
        return ret;
    }

}

Test:

public class Test
{
    private static final String WS_URL = "https://some_server/path/to/ws";

    public static void main(String[] args) throws Exception
    {
        HttpWSXmlClient client = new HttpWSXmlClient(WS_URL);
        client.readRequest("request.xml");
        client.sendAndRecieve(System.out);
    }
}

I got the following output:

Exception in thread "Main Thread" 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 com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1591)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1035)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:124)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1096)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1107)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:415)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
    at com.se.swstest.HttpWSXmlClient.sendAndRecieve(HttpWSXmlClient.java:63)
    at com.se.swstest.Test.main(Test.java:11)
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:285)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:191)
    at sun.security.validator.Validator.validate(Validator.java:218)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1014)
    ... 12 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:174)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:280)
    ... 18 more

Do I need any certificate to be put at jdk/jre/lib/security??? Also, I have a xxx_IE.crt and xxx_FX.crt (for Firefox and IE respectively, and they don't work for the above Java client, so do I need a specific certificate for the Java client?

Thanks.

Richy answered 31/10, 2010 at 8:9 Comment(1)
This may help: #9211014Siftings
H
31

You need to set certificate to hit this url. use below code to set keystore:

System.setProperty("javax.net.ssl.trustStore","clientTrustStore.key");

System.setProperty("javax.net.ssl.trustStorePassword","qwerty");
Hamrah answered 10/5, 2013 at 4:43 Comment(6)
This actually worked for me as I set the trust store to be my whatever.jks file, although I'm not sure why. But thank you.Oubre
worked well here..althought i don't even know what trustStore is :/Passerine
The truststore doesn't contain keys, so naming it .key is senseless.Duong
Can someone tell what might be the real values of qwerty and clientTrustStore.key ?Lobe
I have to point-out that setting the truststore for the whole JVM is a Really Bad Idea. It may hijack settings another component is expecting and, depending upon what you put into your trust store, may reduce the security of any code running within that JVM. It's must better to set the trust store (and related configuration) only for the connections you intend to make. You are using HttpsURLConnection, and you can set the trust managers on those connections without too much trouble.Carree
@Lobe trustStore could be "jssecacerts" or a .jks file stored on the server. Worked for me with the jssecacerts file generated with entpnerd's answerGeriatrician
M
24

Java 8 Solution: I just had this problem and solved it by adding the remote site's certificate to my Java keystore. My solution was based on the solution at the myshittycode blog, which was based on a previous solution in mykong's blog. These blog article solutions boil down to downloading a program called InstallCert, which is a Java class you can run from the command line to obtain the certificate. You then proceed to install the certificate in Java's keystore.

The InstallCert Readme worked perfectly for me. You just need to run the following commands:

  1. javac InstallCert.java
  2. java InstallCert [host]:[port] (Enter the given list number of the certificate you want to add in the list when you run the command - likely just 1)
  3. keytool -exportcert -alias [host]-1 -keystore jssecacerts -storepass changeit -file [host].cer
  4. sudo keytool -importcert -alias [host] -keystore [path to system keystore] -storepass changeit -file [host].cer

See the referenced README file for an example if need be.

Matrices answered 7/4, 2016 at 0:9 Comment(5)
Note that the system's keystore is probably at: $JAVA_HOME/jre/lib/security/cacerts.Matrices
Is there a way to point the Java application to the .cer file instead of modifying the system's keystore?Geriatrician
@Geriatrician I don't believe so (see this Oracle page and this question). That being said, your question might be a worthwhile new question for the StackOverflow community.Matrices
I think I have it working combining your answer (which generates jssecacerts file) file and @Vipin Kumar's answer (to point the application to that file)Geriatrician
I use InstallCerts.java as mentioned but instead of updating JDK keystore wherever the application is deployed I bundle the keystore (file called jssecacerts) made from InstallCert.java together with the application. Just put it on the classpath. This way it will without need for updating the JDK.Mayhew
S
10

I've run into this a few times and it was due to a certificate chain being incomplete. If you are using the standard java trust store, it may not have a certificate that is needed to complete the certificate chain which is required to validate the certificate of the SSL site you are connecting to.

I ran into this problem with some DigiCert certificates and had to manually add the intermediary cert myself.

Seiter answered 12/7, 2013 at 2:18 Comment(4)
I have a DigiCert too and I guess have the same problem you described. In my case I am not able to export from the browser the entire certificate chain to add all of them to the trust store. Could you share the way you did?Shaker
What browser are you using? I believe I used Firefox to do it although I do not remember the exact method.Seiter
I used IE before. Now I could export it with FireFox, added successfully to the trust store but still having the same exception. I will continue trying. Thanks anyway!Shaker
Actually Webserver should send all the certificates in the chain. Is it a problem of Java Client implementation that cannot deal with that? Do you know details on that?Trentontrepan
L
7

Here is the solution that I used for installing a site's public cert into the systems keystore for use.

Download the certificate with the following command:

unix, linux, mac

openssl s_client -connect [host]:[port|443] < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > [host].crt

windows

openssl s_client -connect [host]:[port|443] < NUL | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > [host].crt

That will create a crt that can be used to import into a keystore.

Install the new certificate with the command:

keytool -import -alias "[host]" -keystore [path to keystore] -file [host].crt

This will allow you to import the new cert from the site that is causing the exception.

Lon answered 21/2, 2017 at 20:29 Comment(2)
Many modern servers use SNI; if so for s_client to get the correct cert(s) add -servername $host. In java7 up, -importcert can handle extraneous text in a PEM file so you don't need the sed; also in j7 up, keytool -printcert -sslserver $host[:$port] -rfc fetches the cert chain and prints in PEM (like s_client -showcerts without the extraneous text) which is easier if you don't have OpenSSL installed e.g. Windows.Dwarfish
Your Windows command snippet is using sed so isn't going to work on WindowsInterject
M
7

I had hit this when I was trying to initiate a SOAP request from Java code. What worked for me was:

  1. Get the Server certificate by hitting the URL in browser: http://docs.bvstools.com/home/ssl-documentation/exporting-certificate-authorities-cas-from-a-website This link has all the steps to get the server certificate

  2. Once you have the server certificate with you follow http://java.globinch.com/enterprise-java/security/pkix-path-building-failed-validation-sun-security-validatorexception/#Valid-Certification-Path-to-Requested-Target .

Copying the text from the link, in case this link dies:

All you need to do to fix this error is to add the server certificate to your trusted Java key store. First You need to download the document from the server.

Once you have the certificate in your hard drive you can import it to the Java trust store. To import the certificate to the trusted Java key store, you can use the java ‘keytool‘ tool. On command prompt navigate to JRE bin folder, in my case the path is : C:\Program Files\Java\jdk1.7.0_75\jre\bin . Then use keytool command as follows to import the certificate to JRE.

keytool -import -alias _alias_name_ -keystore ..\lib\security\cacerts -file _path_to_cer_file

It will ask for a password. By default the password is “changeit”. If the password is different you may not be able to import the certificate.

Methylal answered 1/3, 2017 at 8:33 Comment(0)
P
6

This error can also happen if the server only sends its leaf certificate and does not send all the chain certificates needed to build the trust chain to the root CA. Unfortunately this is a common misconfiguration of servers.

Most browsers work around this problem if they already know the missing chain certificate from earlier visits or maybe download the missing certificate if the leaf certificate contains a URL for CA issuers in Authority Information Access (AIA). But this behavior is usually restricted to desktop browsers and other tools simply fail because they cannot build the trust chain.

You can make the JRE to automatically download the intermediate certificate by setting com.sun.security.enableAIAcaIssuers to true

To verify if the server is sending all the chain certificates you can enter the host in the following SSL certificate validation tool https://www.digicert.com/help/

Plantagenet answered 25/3, 2020 at 15:21 Comment(0)
F
3

On Mac OS I had to open the server's self-signed certificate with system Keychain Access tool, import it, dobubleclick it and then select "Always trust" (even though I set the same in importer). Before that, of course I ran java key took with -importcert to import same file to cacert storage.

Fructify answered 3/7, 2015 at 14:53 Comment(0)
N
2

Thank to @danny-paul answer (https://mcmap.net/q/36036/-pkix-path-building-failed-unable-to-find-valid-certification-path-to-requested-target), i resolved my problem on self-hosted instance.

I use Birt application with Tomcat on Windows Server and it was not able to download pictures from Apache service.

I noticed my Apache configuration was incomplete on Windows Server. The third line was commented out. I uncommented it, restart Apache and it works.

SSLEngine on
SSLCertificateFile "c:/Apache24/conf/ssl/cert.pem"
SSLCertificateKeyFile "c:/Apache24/conf/ssl/privkey.pem"
SSLCACertificateFile "c:/Apache24/conf/ssl/chain.pem"
Nations answered 3/5, 2022 at 12:55 Comment(0)
G
1

I faced this issue after migrating from Tomcat7 to Tomcat9. The issue was in Tomcat9/lib/setenv.sh file Following property was incorrectly set to true

-Djsse.enableSNIExtension=false

Updating it to false resolved the issue for me.

Girhiny answered 22/5, 2023 at 10:43 Comment(0)
H
0

For me, I had encountered this error when invoking a webservice call, make sure that the site has a valid ssl, i.e the logo on the side of the url is checked, otherwise need to add the certificate to trusted key store in your machine

Hardspun answered 24/11, 2014 at 8:22 Comment(0)
M
0

I also faced this type of issue.I am using tomcat server then i put endorsed folder in tomcat then its start working.And also i replaced JDK1.6 with 1.7 then also its working.Finally i learn SSL then I resolved this type of issues.First you need to download the certificates from that servie provider server.then you are handshake is successfull. 1.Try to put endorsed folder in your server Next way 2.use jdk1.7

Next 3.Try to download valid certificates using SSL

Missive answered 25/8, 2015 at 13:12 Comment(1)
This doesn't answer the question. What do you mean by endorsed folder?Stenger
D
0

I had the same problem. You need to set the certificate to hit this URL. use the below code to set Keystore:

 System.setProperty ("javax.net.ssl.trustStore", "NUL");
 System.setProperty ("javax.net.ssl.trustStoreType", "Windows-ROOT");
Dettmer answered 3/7, 2023 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.