113
votes

I connected with VPN to setup the inventory API to get product list and it works fine. Once I get the result from the web-service and i bind to UI. And also I integrated PayPal with my application for make Express checkout when I make a call for payment I'm facing this error. I use servlet for back-end process. Can any one say how to fix this issue?

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
6
what I would like to know, which exact target was in thath case... You get an exception, but you get no information about target, which might be different of what you expect.. I have such case, I am sure my link has certificate, and I am still getting this exceptionante.sabo

6 Answers

164
votes

First, you need to obtain the public certificate from the server you're trying to connect to. That can be done in a variety of ways, such as contacting the server admin and asking for it, using OpenSSL to download it, or, since this appears to be an HTTP server, connecting to it with any browser, viewing the page's security info, and saving a copy of the certificate. (Google should be able to tell you exactly what to do for your specific browser.)

Now that you have the certificate saved in a file, you need to add it to your JVM's trust store. At $JAVA_HOME/jre/lib/security/ for JREs or $JAVA_HOME/lib/security for JDKs, there's a file named cacerts, which comes with Java and contains the public certificates of the well-known Certifying Authorities. To import the new cert, run keytool as a user who has permission to write to cacerts:

keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>

It will most likely ask you for a password. The default password as shipped with Java is changeit. Almost nobody changes it. After you complete these relatively simple steps, you'll be communicating securely and with the assurance that you're talking to the right server and only the right server (as long as they don't lose their private key).

18
votes

Now I solved this issue in this way,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream; 

// Create a trust manager that does not validate certificate chains like the default 

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
        }
};

// Install the all-trusting trust manager
try 
{
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} 
catch (Exception e) 
{
    System.out.println(e);
}

Of course this solution should only be used in scenarios, where it is not possible to install the required certifcates using keytool e.g. local testing with temporary certifcates.

11
votes

Whenever we are trying to connect to URL,

if server at the other site is running on https protocol and is mandating that we should communicate via information provided in certificate then we have following option:

1) ask for the certificate(download the certificate), import this certificate in trustore. Default trustore java uses can be found in \Java\jdk1.6.0_29\jre\lib\security\cacerts, then if we retry to connect to the URL connection would be accepted.

2) In normal business cases, we might be connecting to internal URLS in organizations and we know that they are correct. In such cases, you trust that it is the correct URL, In such cases above, code can be used which will not mandate to store the certificate to connect to particular URL.

for the point no 2 we have to follow below steps :

1) write below method which sets HostnameVerifier for HttpsURLConnection which returns true for all cases meaning we are trusting the trustStore.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) write below method, which calls doTrustToCertificates before trying to connect to URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

This call will return response code = 200 means connection is successful.

For more detail and sample example you can refer to URL.

0
votes

SSLHandshakeException can be resolved 2 ways.

  1. Incorporating SSL

    • Get the SSL (by asking the source system administrator, can also be downloaded by openssl command, or any browsers downloads the certificates)

    • Add the certificate into truststore (cacerts) located at JRE/lib/security

    • provide the truststore location in vm arguments as "-Djavax.net.ssl.trustStore="

  2. Ignoring SSL

    For this #2, please visit my other answer on another stackoverflow website: How to ingore SSL verification Ignore SSL Certificate Errors with Java

-1
votes

I believe that you are trying to connect to a something using SSL but that something is providing a certificate which is not verified by root certification authorities such as verisign.. In essence by default secure connections can only be established if the person trying to connect knows the counterparties keys or some other verndor such as verisign can step in and say that the public key being provided is indeed right..

ALL OS's trust a handful of certification authorities and smaller certificate issuers need to be certified by one of the large certifiers making a chain of certifiers if you get what I mean...

Anyways coming back to the point.. I had a similiar problem when programming a java applet and a java server ( Hopefully some day I will write a complete blogpost about how I got all the security to work :) )

In essence what I had to do was to extract the public keys from the server and store it in a keystore inside my applet and when I connected to the server I used this key store to create a trust factory and that trust factory to create the ssl connection. There are alterante procedures as well such as adding the key to the JVM's trusted host and modifying the default trust store on start up..

I did this around two months back and dont have source code on me right now.. use google and you should be able to solve this problem. If you cant message me back and I can provide you the relevent source code for the project .. Dont know if this solves your problem since you havent provided the code which causes these exceptions. Furthermore I was working wiht applets thought I cant see why it wont work on Serverlets...

P.S I cant get source code before the weekend since external SSH is disabled in my office :(

-7
votes

Now I solved this issue in this way,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;
// Create a trust manager that does not validate certificate chains like the 
default TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
    }
};
// Install the all-trusting trust manager
try {
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
    System.out.println(e);
}