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

时间:2012-02-09 12:05:02

标签: java ssl keytool

我有一个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并退出了'post'。
  5. 我仍然遇到同样的错误。

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

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

13 个答案:

答案 0 :(得分:130)

不幸的是 - 它可能是很多东西 - 许多应用服务器和其他java'包装器'都倾向于使用属性和他们自己的“钥匙链”而不是。所以它可能正在寻找完全不同的东西。

没有桁架 - 我会尝试:

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

看看是否有帮助。除了'all'之外,还可以将其设置为'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

答案 1 :(得分: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);
    }
    }

}

答案 2 :(得分:5)

sbt 我遇到了同样的问题 它试图通过ssl从 repo1.maven.org 获取依赖关系 但是说它“无法找到有效的证书路径来请求目标网址” 所以我跟着this post 仍未能验证连接。
所以我读到了它,发现根证书是不够的,正如帖子所建议的那样,所以 - 对我有用的东西是将中间CA证书导入密钥库
我实际上在链中添加了所有证书,它就像一个魅力。

答案 3 :(得分:5)

(从my other response重新发布)
使用java软件分发中的cli实用程序 keytool 导入(以及信任!)所需的证书

示例:

  1. 从cli change dir改为jre \ bin

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

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

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

  5. 不小心我发现了这么简单的小费。 其他解决方案需要使用InstallCert.Java和JDK

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

答案 4 :(得分:3)

从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映像,因此可以使用多阶段构建来快速完成此操作
    • 我正在使用jlink作为/opt/jdk/bin/jlink \ --module-path /opt/jdk/jmods...来构建最小的JRE

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

# 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

答案 5 :(得分:1)

我正在www.udemy.com(REST Java Web Services)上编写REST Web服务教程。教程中的示例说,为了拥有SSL,我们必须有一个名为&#34; trust_store&#34;的文件夹。在我的日食&#34;客户&#34;应包含&#34;密钥库的项目&#34; file(我们有一个&#34;客户&#34;项目来调用服务,&#34;服务&#34;包含REST Web服务的项目 - 在同一个eclipse工作区中的2个项目,一个是客户端,另一个是服务)。为了简单起见,他们说要复制&#34; keystore.jks&#34;来自glassfish应用服务器(glassfish \ domains \ domain1 \ config \ keystore.jsk)我们正在使用并将其放入此信息&#34; trust_store&#34;他们让我在客户端项目中创建的文件夹。这似乎是有道理的:服务器的key_store中的自签名证书将对应于客户端trust_store中的证书。现在,这样做,我得到了原帖提到的错误。我用google搜索了这个错误,因为&#34; keystore.jks&#34;不包含可信/签名证书的客户端上的文件,它找到的证书是自签名的。

为了清楚地说明,让我说,据我所知,&#34; keystore.jsk&#34;包含自签名证书和&#34; cacerts.jks&#34;文件包含CA证书(由CA签名)。 &#34; keystore.jks&#34;是&#34; keystore&#34;和&#34; cacerts.jks&#34;是&#34;信任商店&#34;。作为评论者,Bruno&#34;,上面说,&#34; keystore.jks&#34;是本地的,&#34; cacerts.jks&#34;适用于远程客户。

所以,我对自己说,嘿,玻璃鱼也有&#34; cacerts.jks&#34; file,是glassfish的trust_store文件。 cacerts.jsk应该包含CA证书。显然,我需要我的trust_store文件夹包含一个至少有一个CA证书的密钥库文件。所以,我试着把&#34; cacerts.jks&#34;文件在&#34; trust_store&#34;我在我的客户端项目上创建的文件夹,并将VM属性更改为指向&#34; cacerts.jks&#34;而不是&#34; keystore.jks&#34;。这摆脱了错误。我想它所需要的只是一个CA证书。

这对于生产来说可能并不理想,甚至不仅仅是为了开发而不仅仅是为了工作。例如,您可以使用&#34; keytool&#34;命令将CA证书添加到&#34; keystore.jks&#34;在客户端中的文件。但无论如何,希望这至少可以缩小可能出现在这里导致错误的可能情况。

另外:我的方法似乎对客户端有用(添加到客户端trust_store的服务器证书),看起来上面用于解析原始帖子的注释对服务器(添加到服务器trust_store的客户端证书)很有用。欢呼声。

Eclipse项目设置:

  • MyClientProject
  • src
  • 测试
  • JRE系统库
  • ...
  • trust_store
    --- cacerts.jks --- keystore.jks
来自MyClientProject.java文件的

Snippet:

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

  });
}

答案 6 :(得分:1)

我在我的 pom.xml 文件中添加了这个,它对我有用

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>
        </plugins>
</build>

答案 7 :(得分:1)

我在尝试从使用自签名证书的应用程序访问 https 网址时遇到此错误。 他们提供的是一个 .cert 文件,我不知道把它放在哪里。我通过以下方式解决了它:

keytool 位置在 JDK/bin 文件夹下

方法 1:将证书添加到默认 Java 信任库 - cacerts:

keytool -import -alias myCert -file C://certificate.cert -keystore C://Program Files//Java//jdk1.8.0_271//jre//lib//security//cacerts

密码:changeit

方法二:

创建信任存储:

keytool -import -alias myCert -file C://certificate.cert -keystore myTrustStore

提示如下,可填写为:

Enter keystore password:changeit
Re-enter new password:changeit
Trust this certificate?yes

这将在您运行此命令的文件夹中创建一个 myTrustStore 文件。 将此“mytrustStore”复制到方便的位置。

使用信任存储:

在运行应用程序/服务器时传递这些 JVM 参数:

-Djavax.net.ssl.trustStore=C://myTrustStore -Djavax.net.ssl.trustStorePassword=changeit

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

在我的情况下,我遇到了问题,因为在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找到了您的标记服务器

答案 11 :(得分:0)

就我而言,连接到AWS Gov Postgres RDS时出错。 GOV RDS CA证书有单独的链接-https://s3.us-gov-west-1.amazonaws.com/rds-downloads/rds-combined-ca-us-gov-bundle.pem

将此pem证书添加到Java的证书中。您可以使用以下脚本。

-------- WINDOWDS步骤---------

  1. 使用VSCODE编辑器并安装openssl,keytool插件
  2. 在C:/ rds-ca中创建一个目录
  3. 在目录文件“ rd-ca”内将“ cacerts”文件及其下方脚本文件-“ addCerts.sh”放置
  4. 从vscode运行: 4.1 cd / c / rds-ca / 4.2 ./addCerts.sh
  5. 将cacerts复制到$ {JAVA_HOME} / jre / lib / security

脚本代码:

#!/usr/bin/env sh

OLDDIR="$PWD"

CACERTS_FILE=cacerts

cd /c/rds-ca

echo "Downloading RDS certificates..."

curl  https://s3.us-gov-west-1.amazonaws.com/rds-downloads/rds-combined-ca-us-gov-bundle.pem > rds-combined-ca-bundle.pem

csplit -sk rds-combined-ca-bundle.pem "/-BEGIN CERTIFICATE-/" "{$(grep -c 'BEGIN CERTIFICATE' rds-combined-ca-bundle.pem | awk '{print $1 - 2}')}"

for CERT in xx*; do
    # extract a human-readable alias from the cert
    ALIAS=$(openssl x509 -noout -text -in $CERT |
                   perl -ne 'next unless /Subject:/; s/.*CN=//; print')
    echo "importing $ALIAS"
    
    keytool -import \
            -keystore  $CACERTS_FILE \
            -storepass changeit -noprompt \
            -alias "$ALIAS" -file $CERT
done

cd "$OLDDIR"
echo "$NEWDIR"

答案 12 :(得分:-4)

假设您在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