编辑:我试图在我的博客上以更得体的方式格式化问题和接受的答案。

这是最初的问题。

我得到这个错误:

详细消息sun.security.validator.ValidatorException: PKIX路径 构建失败: provider.certpath. suncertpathbuilderexception:不能 找到请求目标的有效认证路径 导致javax.net.ssl.SSLHandshakeException: validatorexception: PKIX路径构建 失败:sun.security.provider.certpath.SunCertPathBuilderException: 无法找到请求目标的有效认证路径

我使用Tomcat 6作为web服务器。我在同一台机器上的不同端口的不同Tomcats上安装了两个HTTPS web应用程序。假设App1(端口8443)和App2(端口443)。App1连接到App2。当App1连接到App2时,我得到上述错误。我知道这是一个非常常见的错误,所以在不同的论坛和网站上找到了许多解决方案。我在两个Tomcats的server.xml中有以下条目:

keystoreFile="c:/.keystore" 
keystorePass="changeit"

每个站点都说明了app2给出的证书不在app1 jvm的可信存储区中的相同原因。这似乎也是真的,当我试图在IE浏览器中点击相同的URL,它工作(与升温,有一个问题与此网站的安全证书。在这里我说继续这个网站)。但是当相同的URL被Java客户端(在我的情况下)击中时,我得到上述错误。为了把它放到信任库中,我尝试了以下三个选项:

选项1

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

选项2

在环境变量中设置如下

CATALINA_OPTS -- param name
-Djavax.net.ssl.trustStore=C:\.keystore -Djavax.net.ssl.trustStorePassword=changeit ---param value

选项3

在环境变量中设置如下

JAVA_OPTS -- param name
-Djavax.net.ssl.trustStore=C:\.keystore -Djavax.net.ssl.trustStorePassword=changeit ---param value

结果

但是什么都不管用。

最后工作是执行Java方法建议如何处理无效的SSL证书与Apache HttpClient?通过Pascal Thivent,即执行程序InstallCert。

但这种方法适用于开发盒设置,但我不能在生产环境中使用它。

我想知道为什么上面提到的三种方法都不工作,而我已经通过设置在App2服务器的server.xml中提到了相同的值,在truststore中也提到了相同的值

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore")和System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

在App1程序中。

要了解更多信息,这是我如何建立联系:

URL url = new URL(urlStr);

URLConnection conn = url.openConnection();

if (conn instanceof HttpsURLConnection) {

  HttpsURLConnection conn1 = (HttpsURLConnection) url.openConnection();
  
  conn1.setHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
      return true;
    }
  });

  reply.load(conn1.getInputStream());

当前回答

Make sure of your JVM location. There can be half a dozen JREs on your system. Which one is your Tomcat really using? Anywhere inside code running in your Tomcat, write println(System.getProperty("java.home")). Note this location. In <java.home>/lib/security/cacerts file are the certificates used by your Tomcat. Find the root certificate that is failing. This can be found by turning on SSL debug using -Djavax.net.debug=all. Run your app and note from console ssl logs the CA that is failing. Its url will be available. In my case I was surprised to find that a proxy zscaler was the one which was failing, as it was actually proxying my calls, and returning its own CA certificate. Paste url in browser. Certificate will get downloaded. Import this certificate into cacerts using keytool import.

其他回答

您需要将App2的证书添加到所使用的JVM的信任库文件中,该文件位于$JAVA_HOME\lib\security\cacerts。

首先,您可以通过运行以下命令检查您的证书是否已经在信任存储库中: keytool -list -keystore "$JAVA_HOME/jre/lib/security/cacerts"(不需要提供密码)

如果您的证书丢失,您可以通过浏览器下载它,并使用以下命令将其添加到信任存储区:

keytool -import -noprompt -trustcacerts -alias <AliasName> -file   <certificate> -keystore <KeystoreFile> -storepass <Password>

例子:

keytool -import -noprompt -trustcacerts -alias myFancyAlias -file /path/to/my/cert/myCert.cer -keystore /path/to/my/jdk/jre/lib/security/cacerts/keystore.jks -storepass changeit

导入后,您可以再次运行第一个命令来检查是否添加了证书。

Sun/Oracle的信息可以在这里找到。

可部署解决方案(Alpine Linux)

为了能够在我们的应用程序环境中修复这个问题,我们准备了如下的Linux终端命令:

cd ~

将在主目录中生成证书文件。

apk add openssl

该命令在alpine Linux中安装openssl。您可以找到其他Linux发行版的适当命令。

openssl s_client -connect <host-dns-ssl-belongs> < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > public.crt

生成所需的证书文件。

sudo $JAVA_HOME/bin/keytool -import -alias server_name -keystore $JAVA_HOME/lib/security/cacerts -file public.crt -storepass changeit -noprompt

使用'keytool'程序将生成的文件应用到JRE。

注意:请将您的DNS替换为<host- DNS -ssl-belong >

注意:请注意-noprompt不会提示验证信息(yes/no), -storepass changeit参数将禁用密码提示并提供所需的密码(默认为'changeit')。这两个属性将允许您在应用程序环境中使用这些脚本,例如构建Docker映像。

注3:如果你是通过Docker部署你的应用程序,你可以生成一次秘密文件,并把它放在你的应用程序项目文件中。您不需要一次又一次地生成它。

我想加入进来,因为我有一个QEMU环境,我必须在其中下载java文件。事实证明,QEMU中的/etc/ssl/certs/java/cacerts确实存在问题,因为它与主机环境中的/etc/ssl/certs/java/cacerts不匹配。主机环境位于公司代理的后面,因此java cacerts是一个定制版本。

如果您正在使用QEMU环境,请首先确保主机系统可以访问文件。例如,您可以先在您的主机上尝试此脚本。如果脚本在主机上运行得很好,但在QEMU上却不行,那么您也遇到了和我一样的问题。

为了解决这个问题,我必须对QEMU中的原始文件进行备份,将主机环境中的文件复制到QEMU chroot jail中,然后java才能在QEMU中正常下载文件。

更好的解决方案是将/etc挂载到QEMU环境中;但是我不确定其他文件是否会在这个过程中受到影响。所以我决定使用这个丑陋但简单的变通方法。

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到请求目标的有效认证路径

•当我得到错误时,我尝试谷歌表达式的含义,我发现,当服务器更改其HTTPS SSL证书时,会发生此问题,而我们的旧版本的java不识别根证书颁发机构(CA)。

•如果您可以在浏览器中访问HTTPS URL,则可以更新Java以识别根CA。

•在浏览器中,转到Java无法访问的HTTPS URL。单击HTTPS证书链(ie浏览器中有锁图标),单击锁即可查看证书。

•进入证书的“详细信息”和“复制到文件”。复制为Base64 (.cer)格式。它将保存在您的桌面上。

•安装证书时忽略所有警告。

•这就是我如何收集我试图访问的URL的证书信息。

现在我必须让我的java版本知道这个证书,这样它就不会拒绝识别URL了。在这方面,我必须提到,我搜索到根证书信息默认保存在JDK的\jre\lib\security位置,默认访问密码是:changeit。

查看cacerts信息的操作步骤如下:

•点击开始按钮——>运行

•输入cmd。打开命令提示符(您可能需要以管理员身份打开它)。

•进入Java/jreX/bin目录

•输入以下内容

keytool -list -keystore D:\Java\jdk1.5.0_12\jre\lib\security\cacerts

它给出密钥存储库中包含的当前证书的列表。它看起来是这样的:

C:\Documents and Settings\NeelanjanaG>keytool -list -keystore D:\Java\jdk1.5.0_12\jre\lib\security\cacerts

Enter keystore password:  changeit

Keystore type: jks

Keystore provider: SUN

Your keystore contains 44 entries

verisignclass3g2ca, Mar 26, 2004, trustedCertEntry,

Certificate fingerprint (MD5): A2:33:9B:4C:74:78:73:D4:6C:E7:C1:F3:8D:CB:5C:E9

entrustclientca, Jan 9, 2003, trustedCertEntry,

Certificate fingerprint (MD5): 0C:41:2F:13:5B:A0:54:F5:96:66:2D:7E:CD:0E:03:F4

thawtepersonalbasicca, Feb 13, 1999, trustedCertEntry,

Certificate fingerprint (MD5): E6:0B:D2:C9:CA:2D:88:DB:1A:71:0E:4B:78:EB:02:41

addtrustclass1ca, May 1, 2006, trustedCertEntry,

Certificate fingerprint (MD5): 1E:42:95:02:33:92:6B:B9:5F:C0:7F:DA:D6:B2:4B:FC

verisignclass2g3ca, Mar 26, 2004, trustedCertEntry,

Certificate fingerprint (MD5): F8:BE:C4:63:22:C9:A8:46:74:8B:B8:1D:1E:4A:2B:F6

•现在我必须将之前安装的证书包含到cacerts中。

•为此,程序如下:

keytool -import -noprompt -trustcacerts -alias ALIASNAME -file FILENAME_OF_THE_INSTALLED_CERTIFICATE -keystore PATH_TO_CACERTS_FILE -storepass PASSWORD

如果你使用的是Java 7:

keytool -importcert -trustcacerts -alias ALIASNAME -file PATH_TO_FILENAME_OF_THE_INSTALLED_CERTIFICATE -keystore PATH_TO_CACERTS_FILE -storepass changeit

•然后将证书信息添加到cacert文件中。

这是我为上面提到的异常找到的解决方案!!

查看各种证书内容和通过标准openssl过程生成的证书,我注意到openssl根证书的AutorityKeyIdentifier被设置为本身。也许有一种方法可以克服它……但我不知道……

然后我用Java11和BouncyCastle开发了一个小应用程序来生成根证书和密钥,现在在github上:https://github.com/kendarorg/JavaCaCertGenerator

使用该工具生成的根证书不包含AuthorityKeyIdentifier,可以使用keytool直接安装在cacert存储区上。当我创建然后csr和ext文件的域名,这将验证对cacert存储包含根..再也没有握手异常了!

可能是cacert不允许递归AuthorityKeyIdentifier?我不知道,但我会欣赏一些评论:)