无法找到到请求目标的有效证书路径-即使导入证书后也会出错


205

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

当我尝试发布到服务器时,出现以下错误:

无法找到到请求目标的有效认证路径

在对该问题进行了一些研究之后,我进行了以下工作。

  1. 将我的服务器域名保存为root.cer文件。
  2. 在我的Glassfish服务器的JRE中,运行了此命令:
    keytool -import -alias example -keystore cacerts -file root.cer
  3. 要检查证书是否成功添加到我的cacert中,我这样做:
    keytool -list -v -keystore cacerts
    我可以看到该证书存在。
  4. 然后,我重新启动了Glassfish,并退出了“职位”。

我仍然收到相同的错误。

我有一种感觉,这是因为我的Glassfish实际上并未读取我修改过的cacert文件,而是可能读取了其他文件。

你们中有人遇到过这个问题,可以将我推向正确的方向吗?


1
只是为了澄清“我有一个Java客户端试图使用自签名证书访问服务器。”:您是在谈论使用自签名的客户端证书,不是吗?Glassfish上的连接器设置(特别是信任存储设置)是否有任何特定配置?
布鲁诺

“我有一个Java客户端试图访问带有自签名证书的服务器。”:您正在谈论使用自签名的客户端证书,不是吗?-是的
TheCoder

1
我在Glassfish JVM中找到了2个设置:-Djavax.net.ssl.keyStore = $ {com.sun.aas.instanceRoot} /config/keystore.jks和-Djavax.net.ssl.trustStore = $ {com.sun。 aas.instanceRoot} /config/cacerts.jks。我现在需要通过证书添加到其中之一。您可以确认它是我添加到的密钥库吗?
TheCoder 2012年

4
在服务器上,密钥库用于服务器证书及其私钥(密钥库用于“属于”本地用户)。信任库用于用于验证对远程方的信任的证书。您应该将客户端证书添加到服务器信任库中。(见太,尽管Glassfish的似乎并没有被使用JRE的默认位置。)
布鲁诺

那工作布鲁诺。我将其添加到我的Glassfish信任库中。非常感谢您的帮助。你也是德克
TheCoder 2012年

Answers:


155

不幸的是-可能有很多事情-许多应用程序服务器和其他Java“包装器”都倾向于使用属性,它们的“自己”承担钥匙串,而没有。因此,它可能正在寻找完全不同的东西。

缺少桁架-我会尝试:

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

看看是否有帮助。除了“全部”之外,还可以将其设置为“ ssl”,密钥管理器和信任管理器-这可能对您有帮助。将其设置为“帮助”将在大多数平台上列出如下内容。

无论如何-请确保您完全了解密钥库(在其中您拥有证明自己的身份的私钥和证书)和信任库(确定您信任的人)之间的区别-并且您自己的身份也对根有信任的“链”-这与到根的任何链是分开的,您需要弄清楚您对谁的信任。

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#Debug


6
非常感谢!-Djavax.net.ssl.trustStore = / location_of / trustStore解决了我的问题,调试信息也确实很有帮助。
RHE

5
java -Djavax.net.debug = all -Djavax.net.ssl.trustStore = trustStore ...给出以下错误:错误:找不到或加载主类...
rohith

1
当然,您可能还需要使用-Djavax.net.ssl.trustStorePassword = changeit来指定信任库的密码
user1754036

18

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

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

JAVA FILE:博客中缺少该文件

/*
 * 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);
    }
    }

}

3
此解决方案对我有用,但需要对私有类SavingTrustManager进行一些小的更改:public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0];}
理查德(Richard)

1
@ Richard,@ Paul,@ user6258309我在线程“主”中收到错误Exception java.net.SocketTimeoutException:java.net.SocketInputStream.socketRead0(本机方法)的java.net.SocketInputStream.socketRead0(本地方法)读取超时(未知源)我该如何解决
Renjith Krishnan

5
这种“解决方案”根本上是不安全的。不使用。
罗恩侯爵'18

9
这个答案主要是代码。它没有解释为什么或为什么不起作用。

13

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

通过命令行:

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

或通过Java代码:

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网站上的详细信息。


Spring Boot,Spring Cloud微服务和自签名SSL证书也存在相同的问题。我能够在application.properties中设置keyStore和keyStorePassword并使其开箱即用,但对于trustStore和trustStorePassword却无法做到这一点。这个答案对我来说对信托商店有用。
MateŠimović17年

7

(从我的其他回复中转贴)
使用Java软件发行版中的cli实用工具keytool导入(并信任!)所需的证书。

样品:

  1. 从cli更改目录到jre \ bin

  2. 检查密钥库(在jre \ bin目录中找到的文件)
    keytool -list -keystore .. \ lib \ security \ cacerts
    密码已更改

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

  4. 添加证书(在需要删除文件“ .. \ lib \ security \ cacerts”上的“只读”属性之前),运行:keytool -alias REPLACE_TO_ANY_UNIQ_NAME -import -keystore .. \ lib \ security \ cacerts -file“ r: \ root.crt”

我不小心发现了这样一个简单的提示。其他解决方案需要使用InstallCert.Java和JDK

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


6

我对sbt有同样的问题。
它试图通过ssl 从repo1.maven.org获取依赖关系,
但表示“无法找到到所请求目标URL的有效证书路径”。
因此我关注了这篇文章 ,但仍然无法验证连接。
因此,我阅读了一下,发现根证书不足,正如帖子所建议的那样-
对我有用的是将中间CA证书导入密钥库
实际上,我在链中添加了所有证书,它的工作就像一个魅力。


4

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

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

解决步骤

  • 我删除了JDK 10证书,并将其替换为JDK 8
  • 由于我正在构建Docker映像,因此可以使用多阶段构建来快速完成此操作
    • 我使用的是建立一个最小的JRE jlink/opt/jdk/bin/jlink \ --module-path /opt/jdk/jmods...

所以,这是命令的不同路径和顺序...

# 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

2

我正在访问www.udemy.com(REST Java Web服务)上的REST Web服务教程。本教程中的示例说,要获得SSL,我们必须在eclipse的“ client”项目中有一个名为“ trust_store”的文件夹,其中应包含“密钥库”文件(我们有一个“ client”项目来调用服务) ,以及包含REST Web服务的“服务”项目-在同一个Eclipse工作区中的2个项目,一个为客户端,另一个为服务)。为简单起见,他们说要从我们正在使用的glassfish应用服务器(glassfish \ domains \ domain1 \ config \ keystore.jks)中复制“ keystore.jks”,并将其放入他们让我创建的“ trust_store”文件夹中客户项目。这似乎是有道理的:服务器中的自签名证书 的key_store将与客户端trust_store中的证书相对应。现在,这样做,我得到了原始帖子提到的错误。我已经用谷歌搜索了,并且读到错误是由于客户端上的“ keystore.jks”文件不包含受信任/已签名证书,它发现的证书是自签名的。

为了清楚起见,让我说,据我所知,“ keystore.jks”包含自签名证书,“ cacerts.jks”文件包含CA证书(由CA签名)。“ keystore.jks”是“ keystore”,“ cacerts.jks”是“ trust store”。正如评论员所说的“ Bruno”一样,“ keystore.jks”是本地的,“ cacerts.jks”是远程客户端的。

因此,我对自己说,嘿,glassfish也有“ cacerts.jks”文件,它是glassfish的trust_store文件。cacerts.jsk应该包含CA证书。显然,我需要我的trust_store文件夹包含至少具有一个CA证书的密钥存储文件。因此,我尝试将“ cacerts.jks”文件放在我的客户端项目上的“ trust_store”文件夹中,然后将VM属性更改为指向“ cacerts.jks”而不是“ keystore.jks”。那消除了错误。我想它只需要一个CA证书即可工作。

这可能不适合生产,甚至不只是使工作正常进行的开发。例如,您可能使用“ keytool”命令将CA证书添加到客户端中的“ keystore.jks”文件中。但是无论如何,希望这至少可以缩小可能导致错误的情况。

还:我的方法似乎对客户端很有用(将服务器证书添加到客户端trust_store),看起来上面解决原始帖子的注释对服务器有用(将客户端证书添加到服务器trust_store)。干杯。

Eclipse项目设置:

  • MyClientProject
  • src
  • 测试
  • JRE系统库
  • ...
  • trust_store
    --- cacerts.jks --- keystore.jks

MyClientProject.java文件中的片段:

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");
        }

  });
}

1

我的问题是,通过软件更新将Cloud Access Security Broker NetSkope安装在了我的工作笔记本电脑上。这正在改变证书链,将整个链导入到cacerts密钥库后,我仍然无法通过java客户端连接到服务器。我禁用了NetSkope并能够成功连接。


1

就我而言,我遇到了问题,因为在我的tomcat进程中,使用

-Djavax.net.ssl.trustStore=/pathtosomeselfsignedstore/truststore.jks

我正在将证书导入到JRE / lib / security的cacert中,更改未反映出来。然后,我在下面的命令中执行了操作,其中/tmp/cert1.test包含目标服务器的证书

keytool -import -trustcacerts -keystore /pathtosomeselfsignedstore/truststore.jks -storepass password123 -noprompt -alias rapidssl-myserver -file /tmp/cert1.test

我们可以再次检查证书导入是否成功

keytool -list -v -keystore /pathtosomeselfsignedstore/truststore.jks

并查看是否针对别名Rapidssl-myserver找到了您的Taget服务器


0

检查文件是否$JAVA_HOME/lib/security/cacerts存在!在我的情况下,它不是文件,而是指向它的链接,/etc/ssl/certs/java/cacerts这也是指向它本身的链接(WHAT ???),因此由于这个原因,JVM无法找到该文件。

解决方案: 将真实的cacerts文件(您可以从另一个JDK进行复制)复制到/etc/ssl/certs/java/目录中,它将解决您的问题:)


实际上,JDK维护了一个链接,也许是为了与旧版API,SDK兼容...我成功地做到了这一点...我正在从JDK 8迁移到JDK 10并使用Docker,所以我只需要从图像到另一个...我创建了链接,并以完全相同的方式...rm -f /opt/jdk-minimal/jre/lib/security/cacerts ; ln -s /etc/ssl/certs/java/cacerts /opt/jdk-minimal/jre/lib/security/cacerts
Marcello de Sales

-9

假设您在pom.xml中使用$ {JAVA_HOME}之类的类路径变量。

<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"/>

在目标上,添加类路径变量。即..,-DANT_HOME,-DJAVA_HOME

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

1
类路径和证书彼此无关。
罗恩侯爵

1
我相信您误解了这个问题。
达伍德·伊本·卡里姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.