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



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

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

找不到所请求目标的有效证书路径

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

  1. 将我的服务器域名保存为root.cer文件。

  2. 在我的 Glassfish 服务器的 JRE 中,我运行了这个:

    keytool -import -alias example -keystore cacerts -file root.cer
    
  3. 要检查证书是否已成功添加到我的证书中,我这样做了:

    keytool -list -v -keystore cacerts
    

    我可以看到证书存在。

  4. 然后我重新启动了Glassfish并重试了"帖子"。

我仍然收到相同的错误。

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

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

不幸的是 -

这可能是很多事情 - 许多应用程序服务器和其他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

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

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

JAVA文件:博客中缺少该文件

/*
 * 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 系统属性,特别是指向客户端证书存储。

通过命令行:

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);
    ...

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

(从我的其他回复中重新发布(
使用来自 Java 软件发行版的 cli 实用程序密钥工具导入(并信任!

样本:

  1. 从 cli change dir 到 jre\bin

  2. 检查密钥库(在 jre\bin 目录中找到的文件(
    键工具 -列表 -键库 ..\lib\security\cacerts
    密码已更改

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

  4. 添加证书(在需要删除文件..libsecuritycacerts上的"只读"属性之前(,运行:

    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

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

键工具位置位于 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

密码:更改它

方法2:

创建信任存储:

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

它为您提供以下提示,可以填写为:

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

这将在运行此命令的文件夹中创建一个 myTrustStore 文件。将此"我的信任商店"复制到方便的位置。

使用信任存储:

运行应用程序/服务器时,传递以下 JVM 参数:

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

我对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

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构建一个最小的 JRE,/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

我正在 www.udemy.com(REST Java Web Services(上为REST Web服务编写教程。 教程中的示例说,为了拥有 SSL,我们必须在我的 eclipse"客户端"项目中有一个名为"trust_store"的文件夹,它应该包含一个"密钥存储"文件(我们有一个"客户端"项目来调用服务,以及包含 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"是"信任存储"。 正如评论者"Bruno"在上面所说,"keystore.jks"是本地的,"cacerts.jks"是针对远程客户端的。

所以,我对自己说,嘿,glassfish也有"cacerts.jks"文件,这是glassfish的trust_store文件。 cacerts.jsk应该包含CA证书。 显然,我需要我的trust_store文件夹包含至少具有一个CA证书的密钥存储文件。 因此,我尝试将"cacerts.jks"文件放在我创建的"trust_store"文件夹中,在我的客户端项目中,并将虚拟机属性更改为指向"cacerts.jks"而不是"keystore.jks"。 这摆脱了错误。 我想它所需要的只是一个 CA 证书就可以工作。

这对于生产环境,甚至对于开发来说可能并不理想,而不仅仅是让某些东西工作。 例如,您可以使用"keytool"命令将CA证书添加到客户端中的"keystore.jks"文件中。 但无论如何,希望这至少可以缩小可能导致错误的可能情况。

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

Eclipse 项目设置:

  • 我的客户端项目
  • 来源
  • 测试
  • 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");
        }
  });
}

在这个问题上浪费了很多时间。如果您导入了证书,则可以在此处看到它。

keytool -list -v -keystore $JAVA_HOME/lib/security/cacerts

然后使用以下命令创建一个新的,替换SITE_NAME,SITE_PORT,CERTIFICATE_NAME和路径以保存文件。

echo -n | openssl s_client -connect SITE_NAME:SITE_PORT  
| openssl x509 > /path/to/save/CERTIFICATE_NAME.cert

就我而言,我在使用Spring的Keycloak时遇到了问题。使用此命令创建证书并导入密钥库后,问题已解决,工作正常

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

检查文件$JAVA_HOME/lib/security/cacerts是否存在!就我而言,它不是一个文件,而是指向/etc/ssl/certs/java/cacerts的链接,这也是指向它自己的链接(WHAT???(,因此JVM找不到该文件。

溶液:将真正的cacerts文件(您可以从另一个JDK执行此操作(复制到/etc/ssl/certs/java/目录,它将解决您的问题:)

eclipse/Sts用户的注意事项:

因为 eclipse 使用自己的 JRE,所以您应该将证书添加到它自己的 JRE 密钥库中。我遇到了这个问题,直到我将证书添加到 Sts 的 JRE。

SSL日志:

`javax.net.ssl|DEBUG|1A|restartedMain|2021-12-06 23:04:00.874` IRST|TrustStoreManager.java:113|trustStore is: D:sts-4.12.0.RELEASEpluginsorg.eclipse.justj.openjdk.hotspot.jre.full.win32.x86_64_16.0.2.v20210721-1149jrelibsecuritycacerts
This is the full path: "sts-4.12.0.RELEASEpluginsorg.eclipse.justj.openjdk.hotspot.jre.full.win32.x86_64_16.0.2.v20210721-1149jrelibsecuritycacerts"

就我而言,我遇到了这个问题,因为在我的 tomcat 进程中,特定的密钥库是使用

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

我正在将证书导入JRE/lib/security的证书,并且更改没有反映出来。然后我做了以下命令,其中/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

并查看您的 TAGET 服务器是否针对别名 RapidSSL-MyServer 被发现

就我而言,我在连接到 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 的证书中。您可以使用以下脚本。

------窗口步骤

-------
  1. 使用 VSCODE 编辑器并安装 openssl、keytool 插件
  2. 在 C:/rds-ca 中创建目录
  3. 将"cacerts"文件和下面的脚本文件 - "addCerts.sh"放在目录"RD-CA"中
  4. 从 VSCODE 运行:4.1 cd/c/rds-ca/4.2 ./addCerts.sh
  5. Copy cacerts to ${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"

SSL 模式

发生在我身上,因为我的配置包含sslmode=verify-full.您可能需要将其更改为 sslmode=require 。或者一个程序化的等价物。

这也适用于从应用程序中使用 SSL/TLS。

例如,IntelliJ IDEA中的数据库客户端。在那里,您需要转到连接属性,SSH/SSL选项卡,并将Mode设置为Require

有关SSL/TLS模式的更多信息,例如:https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/

我在 Windows 10 笔记本电脑上创建了一个 ubuntu,当我尝试从 https://github.com/yahoo/CMAK 站点加载 CMAK 站点下载时遇到了问题。首先,它给出了致命的SSL错误。

openssl s_client -showcerts -servername github.com -connect github.com:443 </dev/null 2>/dev/null | sed -n -e '/BEGIN CERTIFICATE/,/END CERTIFICATE/ p'  > github-com.pem

并使用以下命令(确保为 ca-cert 输入正确的路径(

cat github-com.pem | sudo tee -a /etc/ssl/certs/ca-certificates.crt

然后使用以下语句(确保证书的路径正确(:

git config --global http.sslCAinfo /etc/ssl/certs/ca-certificates.crt

然后我能够下载 CMAK,但在尝试./sbt clean语句时遇到了问题。它给出了一个pkix path错误。原因是我的 cacert 文件中没有存储我的公司颁发证书。

我已经下载了公司证书(谷歌如何做到这一点(,并按照这篇文章将我下载的证书添加到cacert文件中。在./sbt之前再用一次sudo update-ca-certificates。它对我有用。

注意:执行上述步骤时,您可能需要在root和exit之间切换。

这就是我所做的。

我想在Java 10上开发应用程序,我有Eclipse IDE 2020-06。证书导入解决方案对我不起作用。因此,我安装了Java 11 JDK,切换到最新的Eclipse IDE(在Java 11上运行(,并通过在Maven POM下添加以下状态来确保源代码仍然针对Java 10进行编译:

    <maven.compiler.target>1.10</maven.compiler.target>
    <maven.compiler.source>1.10</maven.compiler.source>
    <maven.compiler.release>10</maven.compiler.release>

它就像一个魅力;没有错误。

最新更新