Unable to find valid certification path to requested target - error even after cert imported

前端 未结 10 2020
野的像风
野的像风 2020-11-22 13:12

I have a Java client trying to access a server with a self-signed certificate.

When I try to Post to the server, I get the following error:

un

相关标签:
10条回答
  • 2020-11-22 13:22

    I am working on a tutorial for REST web services at www.udemy.com (REST Java Web Services). The example in the tutorial said that in order to have SSL, we must have a folder called "trust_store" in my eclipse "client" project that should contain a "key store" file (we had a "client" project to call the service, and "service" project that contained the REST web service - 2 projects in the same eclipse workspace, one the client, the other the service). To keep things simple, they said to copy "keystore.jks" from the glassfish app server (glassfish\domains\domain1\config\keystore.jks) we are using and put it into this "trust_store" folder that they had me make in the client project. That seems to make sense: the self-signed certs in the server's key_store would correspond to the certs in the client trust_store. Now, doing this, I was getting the error that the original post mentions. I have googled this and read that the error is due to the "keystore.jks" file on the client not containing a trusted/signed certificate, that the certificate it finds is self-signed.

    To keep things clear, let me say that as I understand it, the "keystore.jks" contains self-signed certs, and the "cacerts.jks" file contains CA certs (signed by the CA). The "keystore.jks" is the "keystore" and the "cacerts.jks" is the "trust store". As "Bruno", a commenter, says above, "keystore.jks" is local, and "cacerts.jks" is for remote clients.

    So, I said to myself, hey, glassfish also has the "cacerts.jks" file, which is glassfish's trust_store file. cacerts.jsk is supposed to contain CA certificates. And apparently I need my trust_store folder to contain a key store file that has at least one CA certificate. So, I tried putting the "cacerts.jks" file in the "trust_store" folder I had made, on my client project, and changing the VM properties to point to "cacerts.jks" instead of "keystore.jks". That got rid of the error. I guess all it needed was a CA cert to work.

    This may not be ideal for production, or even for development beyond just getting something to work. For instance you could probably use "keytool" command to add CA certs to the "keystore.jks" file in the client. But anyway hopefully this at least narrows down the possible scenarios that could be going on here to cause the error.

    ALSO: my approach seemed to be useful for the client (server cert added to client trust_store), it looks like the comments above to resolve the original post are useful for the server (client cert added to server trust_store). Cheers.

    Eclipse project setup:

    • MyClientProject
    • src
    • test
    • JRE System Library
    • ...
    • trust_store
      ---cacerts.jks ---keystore.jks

    Snippet from MyClientProject.java file:

    static {
      // Setup the trustStore location and password
      System.setProperty("javax.net.ssl.trustStore","trust_store/cacerts.jks");
      // comment out below line
      System.setProperty("javax.net.ssl.trustStore","trust_store/keystore.jks");
      System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
      //System.setProperty("javax.net.debug", "all");
    
      // for localhost testing only
      javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() {
            public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
              return hostname.equals("localhost");
            }
    
      });
    }
    
    0 讨论(0)
  • 2020-11-22 13:23

    Unfortunately - it could be many things - and lots of app servers and other java 'wrappers' are prone to play with properties and their 'own' take on keychains and what not. So it may be looking at something totally different.

    Short of truss-ing - I'd try:

    java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=trustStore ...
    

    to see if that helps. Instead of 'all' one can also set it to 'ssl', key manager and trust manager - which may help in your case. Setting it to 'help' will list something like below on most platforms.

    Regardless - do make sure you fully understand the difference between the keystore (in which you have the private key and cert you prove your own identity with) and the trust store (which determines who you trust) - and the fact that your own identity also has a 'chain' of trust to the root - which is separate from any chain to a root you need to figure out 'who' you trust.

    all            turn on all debugging
    ssl            turn on ssl debugging
    
    The   following can be used with ssl:
        record       enable per-record tracing
        handshake    print each handshake message
        keygen       print key generation data
        session      print session activity
        defaultctx   print default SSL initialization
        sslctx       print SSLContext tracing
        sessioncache print session cache tracing
        keymanager   print key manager tracing
        trustmanager print trust manager tracing
        pluggability print pluggability tracing
    
        handshake debugging can be widened with:
        data         hex dump of each handshake message
        verbose      verbose handshake message printing
    
        record debugging can be widened with:
        plaintext    hex dump of record plaintext
        packet       print raw SSL/TLS packets
    

    Source: # See http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#Debug

    0 讨论(0)
  • 2020-11-22 13:25

    Here is the solution , follow the below link Step by Step :

    http://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target/

    JAVA FILE : which is missing from the blog

    /*
     * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     *   - Redistributions of source code must retain the above copyright
     *     notice, this list of conditions and the following disclaimer.
     *
     *   - Redistributions in binary form must reproduce the above copyright
     *     notice, this list of conditions and the following disclaimer in the
     *     documentation and/or other materials provided with the distribution.
     *
     *   - Neither the name of Sun Microsystems nor the names of its
     *     contributors may be used to endorse or promote products derived
     *     from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    
    
    import java.io.*;
    import java.net.URL;
    
    import java.security.*;
    import java.security.cert.*;
    
    import javax.net.ssl.*;
    
    public class InstallCert {
    
        public static void main(String[] args) throws Exception {
        String host;
        int port;
        char[] passphrase;
        if ((args.length == 1) || (args.length == 2)) {
            String[] c = args[0].split(":");
            host = c[0];
            port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
            String p = (args.length == 1) ? "changeit" : args[1];
            passphrase = p.toCharArray();
        } else {
            System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
            return;
        }
    
        File file = new File("jssecacerts");
        if (file.isFile() == false) {
            char SEP = File.separatorChar;
            File dir = new File(System.getProperty("java.home") + SEP
                + "lib" + SEP + "security");
            file = new File(dir, "jssecacerts");
            if (file.isFile() == false) {
            file = new File(dir, "cacerts");
            }
        }
        System.out.println("Loading KeyStore " + file + "...");
        InputStream in = new FileInputStream(file);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(in, passphrase);
        in.close();
    
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf =
            TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[] {tm}, null);
        SSLSocketFactory factory = context.getSocketFactory();
    
        System.out.println("Opening connection to " + host + ":" + port + "...");
        SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            socket.close();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
        } catch (SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        }
    
        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }
    
        BufferedReader reader =
            new BufferedReader(new InputStreamReader(System.in));
    
        System.out.println();
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        for (int i = 0; i < chain.length; i++) {
            X509Certificate cert = chain[i];
            System.out.println
                (" " + (i + 1) + " Subject " + cert.getSubjectDN());
            System.out.println("   Issuer  " + cert.getIssuerDN());
            sha1.update(cert.getEncoded());
            System.out.println("   sha1    " + toHexString(sha1.digest()));
            md5.update(cert.getEncoded());
            System.out.println("   md5     " + toHexString(md5.digest()));
            System.out.println();
        }
    
        System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
        String line = reader.readLine().trim();
        int k;
        try {
            k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
        } catch (NumberFormatException e) {
            System.out.println("KeyStore not changed");
            return;
        }
    
        X509Certificate cert = chain[k];
        String alias = host + "-" + (k + 1);
        ks.setCertificateEntry(alias, cert);
    
        OutputStream out = new FileOutputStream("jssecacerts");
        ks.store(out, passphrase);
        out.close();
    
        System.out.println();
        System.out.println(cert);
        System.out.println();
        System.out.println
            ("Added certificate to keystore 'jssecacerts' using alias '"
            + alias + "'");
        }
    
        private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
    
        private static String toHexString(byte[] bytes) {
            StringBuilder sb = new StringBuilder(bytes.length * 3);
            for (int b : bytes) {
                b &= 0xff;
                sb.append(HEXDIGITS[b >> 4]);
                sb.append(HEXDIGITS[b & 15]);
                sb.append(' ');
            }
            return sb.toString();
        }
    
        private static class SavingTrustManager implements X509TrustManager {
    
        private final X509TrustManager tm;
        private X509Certificate[] chain;
    
        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }
    
        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }
    
        public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
            throw new UnsupportedOperationException();
        }
    
        public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 13:25

    Check if the file $JAVA_HOME/lib/security/cacerts exists! In my case it was not a file but a link to /etc/ssl/certs/java/cacerts and also this was a link to itself (WHAT???) so due to it JVM can't find the file.

    Solution: Copy the real cacerts file (you can do it from another JDK) to /etc/ssl/certs/java/ directory and it'll solve your problem :)

    0 讨论(0)
  • 2020-11-22 13:26

    (repost from my other response)
    Use cli utility keytool from java software distribution for import (and trust!) needed certificates

    Sample:

    1. From cli change dir to jre\bin

    2. Check keystore (file found in jre\bin directory)
      keytool -list -keystore ..\lib\security\cacerts
      Password is changeit

    3. Download and save all certificates in chain from needed server.

    4. Add certificates (before need to remove "read-only" attribute on file "..\lib\security\cacerts"), run: keytool -alias REPLACE_TO_ANY_UNIQ_NAME -import -keystore ..\lib\security\cacerts -file "r:\root.crt"

    accidentally I found such a simple tip. Other solutions require the use of InstallCert.Java and JDK

    source: http://www.java-samples.com/showtutorial.php?tutorialid=210

    0 讨论(0)
  • 2020-11-22 13:28

    Solution when migrating from JDK 8 to JDK 10

    • The certificates are really different
      • JDK 10 has 80, while JDK 8 has 151
    • JDK 10 has been recently added the certs
      • https://dzone.com/articles/openjdk-10-now-includes-root-ca-certificates
      • http://openjdk.java.net/jeps/319

    JDK 10

    root@c339504909345:/opt/jdk-minimal/jre/lib/security #  keytool -cacerts -list
    Enter keystore password:
    Keystore type: JKS
    Keystore provider: SUN
    
    Your keystore contains 80 entries
    

    JDK 8

    root@c39596768075:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts #  keytool -cacerts -list
    Enter keystore password:
    Keystore type: JKS
    Keystore provider: SUN
    
    Your keystore contains 151 entries
    

    Steps to fix

    • I deleted the JDK 10 cert and replaced it with the JDK 8
    • Since I'm building Docker Images, I could quickly do that using Multi-stage builds
      • I'm building a minimal JRE using jlink as /opt/jdk/bin/jlink \ --module-path /opt/jdk/jmods...

    So, here's the different paths and the sequence of the commands...

    # Java 8
    COPY --from=marcellodesales-springboot-builder-jdk8 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts /etc/ssl/certs/java/cacerts
    
    # Java 10
    RUN rm -f /opt/jdk-minimal/jre/lib/security/cacerts
    RUN ln -s /etc/ssl/certs/java/cacerts /opt/jdk-minimal/jre/lib/security/cacerts
    
    0 讨论(0)
提交回复
热议问题