Java: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

945,057

Solution 1

The problem appears when your server has self signed certificate. To workaround it you can add this certificate to the list of trusted certificates of your JVM.

In this article author describes how to fetch the certificate from your browser and add it to cacerts file of your JVM. You can either edit JAVA_HOME/jre/lib/security/cacerts file or run you application with -Djavax.net.ssl.trustStore parameter. Verify which JDK/JRE you are using too as this is often a source of confusion.

See also: How are SSL certificate server names resolved/Can I add alternative names using keytool? If you run into java.security.cert.CertificateException: No name matching localhost found exception.

Solution 2

Here's what reliably works for me on macOS. Make sure to replace example.com and 443 with the actual hostname and port you're trying to connect to, and give a custom alias. The first command downloads the provided certificate from the remote server and saves it locally in x509 format. The second command loads the saved certificate into Java's SSL trust store.

openssl x509 -in <(openssl s_client -connect example.com:443 -prexit 2>/dev/null) -out ~/example.crt
sudo keytool -importcert -file ~/example.crt -alias example -keystore $(/usr/libexec/java_home)/jre/lib/security/cacerts -storepass changeit

Solution 3

I had the same issue with a valid signed wildcard certificate from symantec.

First try running your java application with -Djavax.net.debug=SSL to see what is really going on.

I ended up importing the intermediate certificate which was causing the cert chain to break.

I downloaded the missing intermediate cert from symantec (you can see the download link to the missing cert in the ssl handshake log: http://svrintl-g3-aia.verisign.com/SVRIntlG3.cer in my case).

And I imported the cert in the java keystore. After importing the intermediate certificate my wildcard ssl cert finally started working:

keytool -import -keystore ../jre/lib/security/cacerts -trustcacerts -alias "VeriSign Class 3 International Server CA - G3" -file /pathto/SVRIntlG3.cer

Solution 4

  1. Export the SSL certificate using Firefox. You can export it by hitting the URL in the browser and then select the option to export the certificate. Let's assume the cert file name is your.ssl.server.name.crt
  2. Go to your JRE_HOME/bin or JDK/JRE/bin
  3. Type the command
  4. keytool -keystore ..\lib\security\cacerts -import -alias your.ssl.server.name -file .\relative-path-to-cert-file\your.ssl.server.name.crt
  5. Restart your Java process

Solution 5

@Gabe Martin-Dempesy's answer is helped to me. And I wrote a small script related to it. The usage is very simple.

Install a certificate from host:

> sudo ./java-cert-importer.sh example.com

Remove the certificate that installed already.

> sudo ./java-cert-importer.sh example.com --delete

java-cert-importer.sh

#!/usr/bin/env bash

# Exit on error
set -e

# Ensure script is running as root
if [ "$EUID" -ne 0 ]
  then echo "WARN: Please run as root (sudo)"
  exit 1
fi

# Check required commands
command -v openssl >/dev/null 2>&1 || { echo "Required command 'openssl' not installed. Aborting." >&2; exit 1; }
command -v keytool >/dev/null 2>&1 || { echo "Required command 'keytool' not installed. Aborting." >&2; exit 1; }

# Get command line args
host=$1; port=${2:-443}; deleteCmd=${3:-${2}}

# Check host argument
if [ ! ${host} ]; then
cat << EOF
Please enter required parameter(s)

usage:  ./java-cert-importer.sh <host> [ <port> | default=443 ] [ -d | --delete ]

EOF
exit 1
fi;

if [ "$JAVA_HOME" ]; then
    javahome=${JAVA_HOME}
elif [[ "$OSTYPE" == "linux-gnu" ]]; then # Linux
    javahome=$(readlink -f $(which java) | sed "s:bin/java::")
elif [[ "$OSTYPE" == "darwin"* ]]; then # Mac OS X
    javahome="$(/usr/libexec/java_home)/jre"
fi

if [ ! "$javahome" ]; then
    echo "WARN: Java home cannot be found."
    exit 1
elif [ ! -d "$javahome" ]; then
    echo "WARN: Detected Java home does not exists: $javahome"
    exit 1
fi

echo "Detected Java Home: $javahome"

# Set cacerts file path
cacertspath=${javahome}/lib/security/cacerts
cacertsbackup="${cacertspath}.$$.backup"

if ( [ "$deleteCmd" == "-d" ] || [ "$deleteCmd" == "--delete" ] ); then
    sudo keytool -delete -alias ${host} -keystore ${cacertspath} -storepass changeit
    echo "Certificate is deleted for ${host}"
    exit 0
fi

# Get host info from user
#read -p "Enter server host (E.g. example.com) : " host
#read -p "Enter server port (Default 443) : " port

# create temp file
tmpfile="/tmp/${host}.$$.crt"

# Create java cacerts backup file
cp ${cacertspath} ${cacertsbackup}

echo "Java CaCerts Backup: ${cacertsbackup}"

# Get certificate from speficied host
openssl x509 -in <(openssl s_client -connect ${host}:${port} -prexit 2>/dev/null) -out ${tmpfile}

# Import certificate into java cacerts file
sudo keytool -importcert -file ${tmpfile} -alias ${host} -keystore ${cacertspath} -storepass changeit

# Remove temp certificate file
rm ${tmpfile}

# Check certificate alias name (same with host) that imported successfully
result=$(keytool -list -v -keystore ${cacertspath} -storepass changeit | grep "Alias name: ${host}")

# Show results to user
if [ "$result" ]; then
    echo "Success: Certificate is imported to java cacerts for ${host}";
else
    echo "Error: Something went wrong";
fi;
Share:
945,057
neztreh
Author by

neztreh

Updated on July 08, 2022

Comments

  • neztreh
    neztreh almost 2 years

    I have a class that will download a file from a https server. When I run it, it returns a lot of errors. It seems that I have a problem with my certificate. Is it possible to ignore the client-server authentication? If so, how?

    package com.da;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.CharBuffer;
    import java.util.concurrent.Future;
    
    import org.apache.http.HttpResponse;
    import org.apache.http.client.utils.URIUtils;
    import org.apache.http.impl.nio.client.DefaultHttpAsyncClient;
    import org.apache.http.nio.IOControl;
    import org.apache.http.nio.client.HttpAsyncClient;
    import org.apache.http.nio.client.methods.AsyncCharConsumer;
    import org.apache.http.nio.client.methods.HttpAsyncGet;
    import org.apache.http.nio.client.methods.HttpAsyncPost;
    
    public class RSDDownloadFile {
        static FileOutputStream fos;
    
        public void DownloadFile(String URI, String Request) throws Exception
        {
            java.net.URI uri = URIUtils.createURI("https", "176.66.3.69:6443", -1, "download.aspx",
                    "Lang=EN&AuthToken=package", null);
            System.out.println("URI Query: " + uri.toString());
    
            HttpAsyncClient httpclient = new DefaultHttpAsyncClient();
            httpclient.start();
            try {
                Future<Boolean> future = httpclient.execute(
                        new HttpAsyncGet(uri),
                        new ResponseCallback(), null);
    
                Boolean result = future.get();
                if (result != null && result.booleanValue()) {
                    System.out.println("\nRequest successfully executed");
                } else {
                    System.out.println("Request failed");
                }              
            } 
            catch(Exception e){
                System.out.println("[DownloadFile] Exception: " + e.getMessage());
            }
            finally {
                System.out.println("Shutting down");
                httpclient.shutdown();
            }
            System.out.println("Done");  
    
        }
    
        static class ResponseCallback extends AsyncCharConsumer<Boolean> {
    
            @Override
            protected void onResponseReceived(final HttpResponse response) {
                 System.out.println("Response: " + response.getStatusLine());
                 System.out.println("Header: " + response.toString());
                 try {   
                     //if(response.getStatusLine().getStatusCode()==200)
                         fos = new FileOutputStream( "Response.html" );
                 }catch(Exception e){
                     System.out.println("[onResponseReceived] Exception: " + e.getMessage());
                 }
            }
    
            @Override
            protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
                try
                {
                    while (buf.hasRemaining()) 
                    {
                        //System.out.print(buf.get());
                        fos.write(buf.get());
                    }
                }catch(Exception e)
                {
                    System.out.println("[onCharReceived] Exception: " + e.getMessage());
                }
            }
    
            @Override
            protected void onCleanup() {
                try
                {             
                    if(fos!=null)
                        fos.close();
                }catch(Exception e){
                    System.out.println("[onCleanup] Exception: " + e.getMessage());         
                }
                 System.out.println("onCleanup()");
            }
    
            @Override
            protected Boolean buildResult() {
                return Boolean.TRUE;
            }
    
        }
    }
    

    Errors:

    URI Query: https://176.66.3.69:6443/download.aspx?Lang=EN&AuthToken=package
    Aug 2, 2011 3:47:57 PM org.apache.http.impl.nio.client.NHttpClientProtocolHandler exception
    SEVERE: I/O error: General SSLEngine problem
    javax.net.ssl.SSLHandshakeException: General SSLEngine problem
        at com.sun.net.ssl.internal.ssl.Handshaker.checkThrown(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLEngineImpl.checkTaskThrown(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLEngineImpl.writeAppRecord(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLEngineImpl.wrap(Unknown Source)
        at javax.net.ssl.SSLEngine.wrap(Unknown Source)
        at org.apache.http.impl.nio.reactor.SSLIOSession.doHandshake(SSLIOSession.java:154)
        at org.apache.http.impl.nio.reactor.SSLIOSession.isAppInputReady(SSLIOSession.java:276)
        at org.apache.http.impl.nio.client.InternalClientEventDispatch.inputReady(InternalClientEventDispatch.java:79)
        at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:161)
        at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:335)
        at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
        at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:275)
        at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
        at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:542)
        at java.lang.Thread.run(Unknown Source)
    Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLEngineImpl.fatal(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.net.ssl.internal.ssl.Handshaker$DelegatedTask.run(Unknown Source)
        at org.apache.http.impl.nio.reactor.SSLIOSession.doHandshake(SSLIOSession.java:180)
        ... 9 more
    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(Unknown Source)
        at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
        at sun.security.validator.Validator.validate(Unknown Source)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(Unknown Source)
        ... 16 more
    Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
        at java.security.cert.CertPathBuilder.build(Unknown Source)
        ... 21 more
    onCleanup()
    
    [DownloadFile] Exception: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
    Shutting down
    Done
    
  • AgilePro
    AgilePro over 10 years
    The part about "Certificate validation is a very important part of SSL security" is not necessarily true. SSL gives you two assurances: (1) that your communication is private, and (2) that you are talking to a server which is known to the NSA.(:-) Sometimes you only care about privacy of the conversation, and then a self-signed certification is fine. See social-biz.org/2011/10/16/the-anti-ssl-conspiracy
  • Jonas Bergström
    Jonas Bergström over 9 years
    Btw, I'm using httpasyncclient:4.0.1
  • EnterSB
    EnterSB over 9 years
    I needed something similar, @JonasBergström, your solution with SSLContext help a lot.
  • Cheruvim
    Cheruvim almost 9 years
    this hasn't worked for me. I have the root and the chain cert installed, but Tomcat-7 still reports validatorException caused by "unable to find valid certification path to requested target" any way to debug this?
  • user207421
    user207421 almost 9 years
    Note that this solution is insecure.
  • twcai
    twcai over 8 years
    Thank you Jonas, your solution does solved the problem. But I found it costs a very long time (3 - 5s) to create the first connection, after that every connection only need 300-400 ms.
  • Fr4nz
    Fr4nz almost 8 years
    If asked for a password, use the default cacerts keystore password changeit (stackoverflow.com/a/22782035/1304830). Also be sure to run cmd as administrator.
  • user207421
    user207421 over 7 years
    The problem also appears with a certificate signed by someone else that isn't trusted.
  • user207421
    user207421 over 7 years
    Works for me why? You need to provide an explanation.
  • user207421
    user207421 over 7 years
    @AgilePro SSL gives you four assurances: authentication, privacy, integrity, and the possibilty of authorization. It does not give you any assurance that you are talking to a server known to the NSA. Caring only about privacy without authentication is a contradiction in terms.
  • AgilePro
    AgilePro over 7 years
    @EJP Agree that if you use a client certificate you can get authentication and I suppose the possibility of authorization ... but most uses are not with a client certificate. What would you call the difference between a "self-signed" certificate, and a certificate from a signing authority? Does signing authority give "integrity". My Joke about NSA is that all signing authorities can not positively guarantee independence from everything. Not that paranoid really, but the point is your certificate is ONLY as secret as the signing authority can make it. Self-signed can be more secret.
  • Vishnu Ranganathan
    Vishnu Ranganathan over 7 years
    openssl x509 -in <(openssl s_client -connect example.com:443 -prexit 2>/dev/null) -out ~/example.crt - what is example.crt in the command i have a .pem certificate i need to give that here ??
  • kisna
    kisna over 7 years
    ssllabs.com/ssltest is a savior, just have to compare it with a working cert validation.
  • kisna
    kisna over 7 years
    This was the case:
  • kisna
    kisna over 7 years
    To avoid confusion, run java (or jcurl) with debug parameters to see remote "Certificate chain" in logs, then grep the "CN" in truststore explicitly passed (instead of default) as follows, if not present, you need to add. ssllabs.com/ssltest/analyze.html will show if server side certs has incomplete chain, and includes intermediate certification path certificates that need to be added. -Djavax.net.debug=ssl,handshake -Djavax.net.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.keyStore=our-client-certs -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.trustStore=their-server-certs
  • kisna
    kisna over 7 years
  • Gabe Martin-Dempesy
    Gabe Martin-Dempesy about 7 years
    .crt and .pem are commonly used file extensions for the same file format. If you already have the file, just run the second command and pass it into the -file argument.
  • lepe
    lepe about 7 years
    Works flawlessly. Great job! . This is how it works: start your SSL service (if its not running), and execute the command as explained (e.g. ./java-cert-importer.sh example.com 1234). That's it.
  • Pigritia
    Pigritia almost 7 years
    I had the same issue, this is very useful, but in my case you only had to add the server certificate to cacerts file of the JDK version
  • zbstof
    zbstof almost 7 years
    Great stuff. Only thing is: I had to use latest openssl 1.0.Xx for some reason, old 9.X.Xx wasn't working.
  • Patrik Beck
    Patrik Beck almost 7 years
    This doesn't work with SNI endpoint. For that case you need to add: -servername example.com when fetching the cert
  • RayCh
    RayCh over 6 years
    Brilliant. Works for me in Windows 10 and JDK 1.8 too.
  • user9869932
    user9869932 over 6 years
    Works great. I was getting the error on a Jenkins server connecting to an external API which changes his certificate and fails my builts. This solves my issue
  • user207421
    user207421 almost 6 years
    @AgilePro Using a server certificate authenticates the server, and is required to make SSL secure, as noted in RFC 2246. Certificates are not secret at all: therefore remainder of your comment makes no sense.
  • Dima Fomin
    Dima Fomin over 5 years
    Great! It works! Just don't forget that you could have both jre and jdk, and both their cacerts must be updated
  • java-addict301
    java-addict301 over 5 years
    In my case, the root CA was there but not the next CA down. Adding the next CA down did the trick - thanks.
  • Wolfgang Fahl
    Wolfgang Fahl over 5 years
    Oracle should have provided something like this in the first place or never every created their own horrible SSL solution. SSL certificate handling should be an operating system's job.
  • Jnn
    Jnn about 5 years
    In my case, I'm using Netbeans + Apache Tomcat (integrated), so, adding .cer to the trust store "cacerts" on Jdk/jre (C:\Program Files\Java\jdk1.8.0_152\jre\lib\security) and Jre (C:\Program Files\Java\jre1.8.0_91\lib\security) works for me
  • primehunter
    primehunter almost 5 years
    Giving the -Djavax.net.ssl.trustStore parameter is crucial to ensure, your application is loading the right cacerts file. (If problems occur, I recommend to try this first.) This resolves the case described by Dima Fomin too.
  • The Godfather
    The Godfather almost 5 years
    And what should I do if browser also complains?
  • Amr Ibrahim
    Amr Ibrahim almost 5 years
    try to install the certificate
  • will824
    will824 almost 4 years
    Please keep in mind that there is another tool called portecle which can open the cacert file (certificate store) and import the certificate easily. Just remember to save the cacert file afterwards.
  • Joe - ElasticsearchBook.com
    Joe - ElasticsearchBook.com over 3 years
    Thank you @bhdrk.
  • Nitin
    Nitin over 3 years
    In my case browser is okay, but still, I am getting error in java
  • dhS
    dhS about 3 years
    I did this but somehow it is showing the error log always
  • S.Daineko
    S.Daineko about 3 years
    Thank you very much! I want to make a couple of edits that helped me. 1. Do not forget that when starting from su JAVA_HOME is different 2. In cacertspath = $ {javahome}/lib/security/cacerts, you may need to insert a /jre/, that is, cacertspath =${javahome}/jre/lib/security/cacerts
  • aquawicket
    aquawicket over 2 years
    Are you saying you can just completely remove the truststore and not even have to deal with certificates? If so, I'd love to know, because these certificate issues do nothing but steal my productivity and waste countless hours of my time.
  • Smart Coder
    Smart Coder over 2 years
    if you have keystore then possibly yes. You need to have cert in one of them.