关于java:无法找到所请求目标的有效证书路径 – 即使在导入证书后也会出错

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

我有一个Java客户端试图用一个自签名证书访问一个服务器。

当我尝试将邮件发送到服务器时,会出现以下错误:

unable to find valid certification path to requested target

在对这个问题做了一些研究之后,我做了以下的工作。

  • 将我的服务器域名保存为root.cer文件。
  • 在Glassfish服务器的JRE中,我运行了以下命令:
    keytool -import -alias example -keystore cacerts -file root.cer
  • 为了检查证书是否已成功添加到我的cacert,我执行了以下操作:
    keytool -list -v -keystore cacerts
    我看到证书在。
  • 然后我重新启动Glassfish,退出"岗位"。
  • 我仍然收到同样的错误。

    我有一种感觉,这是因为我的Glassfish并没有真正阅读我修改过的cacert文件,但可能是其他文件。

    你们中有谁有这个问题,能把我推向正确的方向吗?


    不幸的是,它可能有很多东西,而且很多应用服务器和其他Java"包装器"都倾向于玩属性,而他们自己的"钥匙链"则不需要。所以它可能在寻找完全不同的东西。

    缺少桁架-我会尝试:

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

    看看是否有帮助。除了"全部"之外,还可以将其设置为"ssl"、"密钥管理器"和"信任管理器",这在您的情况下可能会有所帮助。将其设置为"帮助"将在大多数平台上列出类似下面的内容。

    无论如何-确保您完全理解密钥存储库(其中您有私钥和证书证明您自己的身份)和信任存储库(决定您信任的人)之间的区别-以及您自己的身份也有一个到根的"信任链"这一事实-它与您需要的任何到根的链分离找出你信任的人。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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

    来源:请参阅http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/jsserefguide.html调试


    以下是解决方案,请按照以下链接逐步操作:

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

    Java文件:从博客中丢失的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    /*
     * 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);
        }
        }

    }


    您需要配置JSSE系统属性,特别是指向客户机证书存储。

    通过命令行:

    1
    java -Djavax.net.ssl.trustStore=truststores/client.ts com.progress.Client

    或通过Java代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import java.util.Properties;
        ...
        Properties systemProps = System.getProperties();
        systemProps.put("javax.net.ssl.keyStorePassword","passwordForKeystore");
        systemProps.put("javax.net.ssl.keyStore","pathToKeystore.ks");
        systemProps.put("javax.net.ssl.trustStore","pathToTruststore.ts");
        systemProps.put("javax.net.ssl.trustStorePassword","passwordForTrustStore");
        System.setProperties(systemProps);
        ...

    有关更多信息,请参阅RedHat网站上的详细信息。


    (从我的其他回复中转发)从Java软件分发中使用CLI实用密钥工具进行导入(信任!)需要的证书

    Sample:

  • 从cli change dir到jrein

  • 检查密钥库(在jrein目录中找到的文件)keytool-list-keystore..libsecuritycacerts密码已更改

  • 从需要的服务器下载并保存链中的所有证书。

  • 添加证书(在需要删除文件"..libsecuritycacerts"上的"read-only"属性之前),运行:keytool-alias replace_to_any_uniq_name-import-keystore..libsecuritycacerts-file"r:
    oot.crt"

  • 我不小心发现了这么简单的小费。其他解决方案需要使用安装Curt.java和JDK。

    资料来源:http://www.java-samples.com/showtutorial.php?TutoRalID=210


    我对SBT也有同样的问题。
    它试图通过ssl从repo1.maven.org获取依赖项
    但表示"无法找到请求的目标URL的有效认证路径"。
    所以我跟着这个帖子仍然无法验证连接。
    因此,我阅读了相关内容,发现根证书不够,正如文章所建议的那样,因此-
    对我有用的是将中间CA证书导入密钥库。
    我实际上把所有的证书都加在了链子里,它很有魅力。


    从JDK 8迁移到JDK 10时的解决方案

    • 证书真的不一样
      • JDK 10有80个,而JDK 8有151个
    • JDK 10最近增加了certs
      • https://dzone.com/articles/openjdk-10-now-includes-root-ca-certificates
      • http://openjdk.java.net/jeps/319

    JDK 10

    1
    2
    3
    4
    5
    6
    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

    1
    2
    3
    4
    5
    6
    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

    修复步骤

    • 我删除了JDK 10证书,并将其替换为JDK 8
    • 因为我正在构建Docker图像,所以我可以使用多阶段构建快速实现这一点。
      • 我正在使用jlink作为/opt/jdk/bin/jlink \
        --module-path /opt/jdk/jmods...
        构建一个最小的JRE。

    下面是不同的路径和命令序列…

    1
    2
    3
    4
    5
    6
    # 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


    我正在做一个关于REST Web服务的教程,在www. udimy.com(REST JavaWeb服务)上。教程中的示例说,为了拥有SSL,我们必须在Eclipse"客户机"项目中有一个名为"信任存储"的文件夹,该文件夹应包含一个"密钥存储"文件(我们有一个"客户机"项目来调用该服务,以及包含其余Web服务的"服务"项目-在同一Eclipse工作区中有两个项目,一个是客户机,另一个是客户机服务)。为了简单起见,他们说要从我们使用的glassfish应用服务器(glassfishdomainsdomain1configkeystore.jsk)中复制"keystore.jks",并将其放入我在客户机项目中创建的"trust-store"文件夹中。这似乎有道理:服务器密钥存储区中的自签名证书将与客户机信任存储区中的证书相对应。现在,这样做,我得到了原始帖子提到的错误。我在谷歌上搜索了这个信息,并阅读到错误是由于客户端上的"keystore.jks"文件不包含可信/签名证书,它找到的证书是自签名的。

    为了保持清晰,让我说,据我所知,"keystore.jsk"包含自签名证书,"ca certs.jks"文件包含CA证书(由CA签名)。"keystore.jks"是"keystore","cacerts.jks"是"信任库"。正如上面评论者所说的"bruno","keystore.jks"是本地的,"cacerts.jks"是远程客户机的。

    所以,我对自己说,嘿,Glassfish也有"cacerts.jks"文件,这是Glassfish的信任库文件。jsk应该包含CA证书。显然,我需要我的信任存储文件夹包含至少有一个CA证书的密钥存储文件。因此,我尝试将"cacerts.jks"文件放在我创建的"trust-store"文件夹中的客户机项目上,并将VM属性更改为指向"cacerts.jks"而不是"keystore.jks"。这样就消除了错误。我想只需要一张证书就行了。

    这可能不适合于生产,甚至不适合于开发,而不仅仅是工作。例如,您可以使用"keytool"命令将CA证书添加到客户机中的"keystore.jks"文件中。但无论如何,希望这至少缩小了可能导致错误的场景。

    另外:我的方法似乎对客户机很有用(服务器证书添加到客户机信任存储),上面解析原始日志的注释对服务器很有用(客户证书添加到服务器信任存储)。干杯。

    Eclipse项目设置:

    • Myclipse项目
    • SRC
    • 测试
    • JRE系统库
    • 信托商店-CACERT.JKSK-SkyStudioJKS

    myclientproject.java文件中的代码段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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");
            }

      });
    }

    检查文件$JAVA_HOME/lib/security/cacerts是否存在!在我的例子中,它不是一个文件,而是一个到/etc/ssl/certs/java/cacerts的链接,而且这也是一个到自身的链接(什么???)因此,由于它,JVM找不到文件。

    解决方案:将真正的cacerts文件(您可以从另一个jdk中复制)复制到/etc/ssl/certs/java/目录,这样就可以解决您的问题:)


    我的问题是一个云访问安全代理netskope通过软件更新安装在我的笔记本电脑上。这改变了证书链,在导入整个Calcts密钥库后,我仍然无法通过Java客户端连接到服务器。我禁用了Netskope并成功连接。


    假设您使用pom.xml中的类路径变量,如$java_home。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <target>
                        <property name="compile_classpath" refid="maven.compile.classpath"/>
                        <property name="runtime_classpath" refid="maven.runtime.classpath"/>
                        <property name="test_classpath" refid="maven.test.classpath"/>
                        <property name="plugin_classpath" refid="maven.plugin.classpath"/>
                        <property name="jaxb-api.jar" value="${maven.dependency.javax.xml.bind.jaxb-api.jar.path}"/>
                        <property name="project_home" value="${PROJECT_HOME}"/>
                        <property name="java_home" value="${JAVA_HOME}"/>
                        <property name="ant_home" value="${ANT_HOME}"/>
                        <property name="common_home" value="${COMMON_HOME}"/>
                        <property name="JAXP_HOME" value="${common_home}/lib"/>
                        <property name="ejfw_home" value="${PROJECT_HOME}/lib"/>
                        <property name="weblogic_home" value="${WL_HOME}"/>
                        <property name="fw_home" value="${FW_HOME}"/>
                        <property name="env" value="${BUILDENV}"/>
                        <property name="tokenfile" value="${BUILDENV}${BUILDENV_S2S}.properties"/>

    在目标中,添加类路径变量。例如,-唐太家,-德贾瓦家

    1
    clean install -e -DPROJECT_HOME=..... -DANT_HOME=C:\bea1036\modules\org.apache.ant_1.7.1 -DJAVA_HOME=C:\bea1036\jdk160_31