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

这是最初的问题。

我得到这个错误:

详细消息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());

当前回答

我们也有同样的问题,我们做了以下所有的事情。

重新导入服务器SSL证书。 确保weblogic使用正确的caecerts。

最后我们的weblogic启用weblogic调试模式,发现有“NOT HANDSHAKED”异常。

我们发现的原因是,客户端系统使用的是jdk 1.6,服务器使用的是更高的jdk版本(1.8),因为有一些TLS版本不匹配导致了这个问题。

Weblogic团队通过在服务器参数中添加以下行来调整服务器配置。

-Djdk.tls.client.protocol=TLSv1.2 -DUseSunHttpHandler=true.

其他回答

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.

对我来说,这个错误也出现在试图连接到一个NGINX反向代理背后的进程,它正在处理SSL。

事实证明,问题在于一个没有连接整个证书链的证书。 当我添加中间证书时,问题就解决了。

希望这能有所帮助。

可以通过编程方式禁用SSL验证。对于开发人员来说非常有效,但不推荐用于生产环境,因为您可能需要在那里使用“真正的”SSL验证,或者安装并使用您自己的可信密钥,然后仍然使用“真正的”SSL验证。

下面的代码为我工作:

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class TrustAnyTrustManager implements X509TrustManager {

  public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
  }

  public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
  }

  public X509Certificate[] getAcceptedIssuers() {
    return null;
  }
}

             HttpsURLConnection conn = null;
             URL url = new URL(serviceUrl);
             conn = (HttpsURLConnection) url.openConnection();
             SSLContext sc = SSLContext.getInstance("SSL");  
             sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());  
                    // Create all-trusting host name verifier
             HostnameVerifier allHostsValid = new HostnameVerifier() {
               public boolean verify(String hostname, SSLSession session) {
                return true;
              }
            };
            conn.setHostnameVerifier(allHostsValid);

或者,如果您不控制下面的连接,您也可以为所有连接覆盖全局SSL验证https://stackoverflow.com/a/19542614/32453

如果你正在使用Apache HTTPClient,你必须“以不同的方式”禁用它:https://stackoverflow.com/a/2703233/32453

可部署解决方案(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部署你的应用程序,你可以生成一次秘密文件,并把它放在你的应用程序项目文件中。您不需要一次又一次地生成它。

我在Apache Tomcat/7.0.67和Java JVM Version: 1.8.0_66-b18上也有同样的问题。随着Java升级到JRE 1.8.0_241,这个问题似乎已经解决了。