解决javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径构建失败错误?

时间:2012-03-08 14:17:14

标签: java ssl https

编辑: - 尝试以更加流畅的方式格式化问题并接受答案 Blog

这是原始问题。

我收到此错误:

  

详细消息sun.security.validator.ValidatorException:PKIX路径   建筑失败:
  sun.security.provider.certpath.SunCertPathBuilderException:无法   找到所请求目标的有效证书路径

     

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

我使用Tomcat 6作为网络服务器。我有两个HTTPS Web应用程序安装在不同端口上但位于同一台机器上的不同Tomcats上。说App1(port 8443)和  App2(port 443)App1App2相关联。当App1连接到App2时,我收到上述错误。我知道这是一个非常常见的错误,所以在不同的论坛和网站上遇到了很多解决方案。我在两个Tomcats的server.xml中都有以下条目:

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

每个网站都说与app2提供的证书不在app1 jvm的受信任存储区中的原因相同。当我试图在IE浏览器中访问相同的URL时,这似乎也是正确的,它可以工作(加热,这个网站的安全证书存在问题。在这里我说继续这个网站)。但是当Java客户端(在我的情况下)遇到相同的URL时,我得到上述错误。所以把它放在信任库中我尝试了这三个选项:

选项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

但没有效果

最后工作正在执行Pascal Thivent在How to handle invalid SSL certificates with Apache HttpClient?中建议的Java方法,即执行程序InstallCert。

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

我在想 当我在server.xml服务器的app2中提到相同的值并通过设置

在信任库中提到相同的值时,为什么上面提到的三种方法都不起作用?

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore") and 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());

27 个答案:

答案 0 :(得分:344)

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

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

如果您的证书丢失,可以通过浏览器下载并使用以下命令将其添加到信任库来获取证书:

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

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

可以找到Sun / Oracle信息here

答案 1 :(得分:160)

  

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证书链(Internet Explorer中有锁定图标),单击锁定以查看证书。

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

•安装证书,忽略所有警报。

•这是我收集我尝试访问的URL的证书信息的方式。

现在我必须让我的java版本知道证书,以便进一步拒绝识别URL。在这方面,我必须提一下,我在谷歌的 \ 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&gt; keytool -list -keystore D:\ Java \ jdk1.5.0_12 \ jre \ lib \ security \ cacerts

     

输入密钥库密码:changeit

     

密钥库类型:jks

     

密钥库提供商:SUN

     

您的密钥库包含44个条目

     

verisignclass3g2ca,2004年3月26日,trustedCertEntry,

     

证书指纹(MD5):A2:33:9B:4C:74:78:73:D4:6C:E7:C1:F3:8D:CB:5C:E9

     

entrustclientca,2003年1月9日,trustedCertEntry,

     

证书指纹(MD5):0C:41:2F:13:5B:A0:54:F5:96:66:2D:7E:CD:0E:03:F4

     

thawtepersonalbasicca,1999年2月13日,trustedCertEntry,

     

证书指纹(MD5):E6:0B:D2:C9:CA:2D:88:DB:1A:71:0E:4B:78:EB:02:41

     

addtrustclass1ca,2006年5月1日,trustedCertEntry,

     

证书指纹(MD5):1E:42:95:02:33:92:6B:B9:5F:C0:7F:DA:D6:B2:4B:FC

     

verisignclass2g3ca,2004年3月26日,trustedCertEntry,

     

证书指纹(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文件中。

这是我在上面提到的Exception中找到的解决方案!!

答案 2 :(得分:30)

如何在Tomcat 7中使用它

我想在Tomcat应用中支持自签名证书,但以下代码段无法正常工作

import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HTTPSPlayground {
    public static void main(String[] args) throws Exception {

        URL url = new URL("https:// ... .com");
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

        httpURLConnection.setRequestMethod("POST");
        httpURLConnection.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
        httpURLConnection.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(httpURLConnection.getOutputStream());

        String serializedMessage = "{}";
        wr.writeBytes(serializedMessage);
        wr.flush();
        wr.close();

        int responseCode = httpURLConnection.getResponseCode();
        System.out.println(responseCode);
    }
}

这就解决了我的问题:

1)下载.crt文件

echo -n | openssl s_client -connect <your domain>:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/<your domain>.crt
  • <your domain>替换为您的域名(例如jossef.com

2)在Java的.crt证书商店

中应用cacerts文件
keytool -import -v -trustcacerts -alias <your domain> -file ~/<your domain>.crt -keystore <JAVA HOME>/jre/lib/security/cacerts -keypass changeit -storepass changeit
  • <your domain>替换为您的域名(例如jossef.com
  • <JAVA HOME>替换为您的java主目录

3)黑客攻击

即使我已在Java的默认证书商店中安装了我的证书,但 Tomcat忽略了(似乎未将其配置为使用Java的默认证书库)。

要破解此问题,请在代码中的某处添加以下内容:

String certificatesTrustStorePath = "<JAVA HOME>/jre/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore", certificatesTrustStorePath);

// ...

答案 3 :(得分:6)

在我的情况下,问题是Web服务器仅发送证书和中间CA,而不发送根CA。 添加此JVM选项可解决问题:-Dcom.sun.security.enableAIAcaIssuers=true

  

支持“授权信息访问”扩展程序的caIssuers访问方法。出于兼容性考虑,默认情况下将其禁用,并且可以通过将系统属性com.sun.security.enableAIAcaIssuers设置为true来启用。

     

如果设置为true,则Sun的CertPathBuilder的PKIX实现将使用证书的AIA扩展名(除了指定的CertStores之外)中的信息来查找颁发CA证书,前提是该证书是ldap,http或ftp类型的URI。

Source

答案 4 :(得分:5)

我的cacerts文件完全是空的。我通过从我的Windows机器(使用Oracle Java 7)复制cacerts文件并将其scp到我的Linux机箱(OpenJDK)来解决这个问题。

cd %JAVA_HOME%/jre/lib/security/
scp cacerts mylinuxmachin:/tmp

然后在linux机器上

cp /tmp/cacerts /etc/ssl/certs/java/cacerts

到目前为止它运作良好。

答案 5 :(得分:5)

另一个原因可能是JDK的过时版本。我使用的是jdk版本1.8.0_60,只需更新到最新版本即可解决证书问题。

答案 6 :(得分:4)

在Linux下使用Tomcat 7,这就行了。

String certificatesTrustStorePath = "/etc/alternatives/jre/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore", certificatesTrustStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

在Linux下,$JAVA_HOME并非始终设置,但通常/etc/alternatives/jre指向$JAVA_HOME/jre

答案 7 :(得分:4)

对我来说,尝试连接到处理SSL的NGINX反向代理后面的进程时也出现了这个错误。

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

希望这有帮助。

答案 8 :(得分:4)

遇到相同问题时,我正在使用jdk1.8.0_171。我在这里尝试了前2个解决方案(使用keytool添加证书,以及另一个具有hack的解决方案),但是它们对我不起作用。

我将我的JDK升级到1.8.0_181,它的工作就像一个魅力。

答案 9 :(得分:2)

我写了一个小的win32(WinXP 32位testet)愚蠢的cmd(命令行)脚本,它在程序文件中查找所有java版本并为它们添加证书。 密码必须是默认的“changeit”或在脚本中自行更改: - )

@echo off

for /F  %%d in ('dir /B %ProgramFiles%\java') do (
    %ProgramFiles%\Java\%%d\bin\keytool.exe -import -noprompt -trustcacerts -file some-exported-cert-saved-as.crt -keystore %ProgramFiles%\Java\%%d\lib\security\cacerts -storepass changeit
)

pause

答案 10 :(得分:2)

这似乎是一个很好的地方,可以记录臭名昭著的PKIX错误消息的另一个可能原因。花了太长时间看了密钥库和信任库的内容以及各种Java安装配置后,我才意识到我的问题归结于...错字。

拼写错误意味着我也将密钥库用作信任库。由于我的公司Root CA在密钥库中并未定义为独立证书,而仅是证书链的一部分,并且在其他任何地方(即cacerts)均未定义,因此我一直收到PKIX错误。

发布失败后(这是prod config,在其他地方还可以),花了两天的时间我终于看到错字了,现在一切都很好了。

希望这对某人有帮助。

答案 11 :(得分:2)

可部署的解决方案(Alpine Linux)

为了能够在我们的应用程序环境中解决此问题,我们准备了Linux终端命令,如下所示:

cd ~

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

apk add openssl

此命令在高山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。

注意:请用<host-dns-ssl-belongs>

替换您的DNS

注意2:请注意,-noprompt不会提示验证消息(是/否),而-storepass changeit参数将禁用密码提示并提供所需的密码(默认是“ changeit”)。这两个属性使您可以在应用程序环境中使用这些脚本,例如构建Docker映像。

注意3 如果您是通过Docker部署应用程序,则可以生成一次密钥文件并将其放入应用程序项目文件中。您无需一次又一次地生成它。

答案 12 :(得分:1)

我正在使用颤振并突然收到此错误。因此,基本上发生的是 android/build.gradle 文件中依赖项中的行,例如:

  classpath 'com.android.tools.build:gradle:4.1.0'
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

需要证明成绩文件是从互联网下载的。但是当有什么东西阻止了 gradle 下载这些证书时,这通常会显示出来。

我尝试导出证书并手动添加它,但它似乎对我不起作用。在无数次头痛之后,对我有用的是从您的网络偏好中禁用代理。有人提到禁用 Charles Proxy 会修复它,但在那一刻,我不知道 Charles 是什么以及什么是代理。就我而言,我没有 Charles 代理,所以我继续在 Mac 的网络首选项设置中查找代理(它可以在 Windows 的网络设置中找到)。我在代理中启用了 Socks。我禁用了它,然后再次重建了 gradle 和 TA-DAH !!!它使黄油光滑。

有几件事要记住。如果您在禁用代理后立即构建项目而未关闭网络首选项选项卡,则禁用代理将不起作用,并且会显示相同的错误。此外,如果您已经构建了该项目并且您在禁用代理后再次运行它,那么它很可能会显示相同的错误(可能是由于 IDE 缓存)。它是如何为我工作的:重新启动 Mac,在浏览器中打开几个选项卡(用于几次网络调用),从系统首选项>> wifi 中检查网络首选项并禁用代理,关闭系统首选项应用程序,然后构建项目。

答案 13 :(得分:1)

对于在Ubuntu服务器上运行的Tomcat,要找出正在使用的Java,请使用&#34; ps -ef | grep tomcat&#34;命令:

样品:

/home/mcp01$ **ps -ef |grep tomcat**
tomcat7  28477     1  0 10:59 ?        00:00:18 **/usr/local/java/jdk1.7.0_15/bin/java** -Djava.util.logging.config.file=/var/lib/tomcat7/conf/logging.properties -Djava.awt.headless=true -Xmx512m -XX:+UseConcMarkSweepGC -Djava.net.preferIPv4Stack=true -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/share/tomcat7/endorsed -classpath /usr/share/tomcat7/bin/bootstrap.jar:/usr/share/tomcat7/bin/tomcat-juli.jar -Dcatalina.base=/var/lib/tomcat7 -Dcatalina.home=/usr/share/tomcat7 -Djava.io.tmpdir=/tmp/tomcat7-tomcat7-tmp org.apache.catalina.startup.Bootstrap start
1005     28567 28131  0 11:34 pts/1    00:00:00 grep --color=auto tomcat

然后,我们可以进入: cd /usr/local/java/jdk1.7.0_15/jre/lib/security

默认 cacerts 文件位于此处。将不受信任的证书插入其中。

答案 14 :(得分:0)

我遇到了使用AndroidStudioCharles ProxyJUnitRobolectric的问题。 谢谢@SimonSez,我可以解决问题。

  1. %JAVA_HOME%-Android使用不同的JavaHome。您可以在Run UnitTests窗口中找到完整路径。以我为例,/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home
  2. 将Charles证书下载到Downloads文件夹中
  3. 运行命令

    sudo keytool -import -noprompt -trustcacerts -alias charles -file ~/Downloads/charles-ssl-proxying-certificate.pem -keystore "/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/jre/lib/security/cacerts" -storepass changeit

The official Charles doc

答案 15 :(得分:0)

下面的MacOS X是对我有用的确切命令,在该示例中,我不得不在'importcert'选项中尝试使用双连字符:

sudo keytool -–importcert -file /PathTo/YourCertFileDownloadedFromBrowserLockIcon.crt -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/security/cacerts -alias "Cert" -storepass changeit

答案 16 :(得分:0)

我在使用代理时遇到了Android Studio的问题。我使用的Crashlytics试图在构建过程中上载映射文件。

我将缺少的代理证书添加到位于以下位置的信任库中 /Users/[username]/Documents/Android Studio.app/Contents/jre/jdk/Contents/Home/jre/lib/security/cacerts

使用以下命令:keytool -import -trustcacerts -keystore cacerts -storepass [password] -noprompt -alias [alias] -file [my_certificate_location]

例如,使用默认信任库密码 keytool -import -trustcacerts -keystore cacerts -storepass changeit -noprompt -alias myproxycert -file /Users/myname/Downloads/MyProxy.crt

答案 17 :(得分:0)

将此添加到您的代码中:

TrustManager[] trustAllCerts = new TrustManager[]{
           new X509TrustManager() {
               @Override
               public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                   return new X509Certificate[0];
               }

               @Override
               public void checkClientTrusted(
                       java.security.cert.X509Certificate[] certs, String authType) {
               }

               @Override
               public void checkServerTrusted(
                       java.security.cert.X509Certificate[] certs, String authType) {
               }
           }
       };

       try {
           SSLContext sc = SSLContext.getInstance("SSL");
           sc.init(null, trustAllCerts, new java.security.SecureRandom());
           HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
       } catch (GeneralSecurityException e) {
       }

答案 18 :(得分:0)

对我来说,https://stackoverflow.com/a/9619478/4507034这个帖子中公认的解决方案不起作用。

相反,我设法通过将证书导入到我的计算机信任的证书中来解决了这个问题。

步骤:

  1. 转到证书无效的网址(例如https://localhost:8443/yourpath)。
  2. 按照提到的帖子中的说明导出证书。
  3. 在Windows计算机上,打开:Manage computer certificates
  4. 转到Trusted Root Certification Authorities-> Certificates
  5. 在此处导入您的your_certification_name.cer文件。

答案 19 :(得分:0)

只是一个小技巧。将文件“ hudson.model.UpdateCenter.xml”中的URL从https更新为http

<?xml version='1.1' encoding='UTF-8'?>
<sites>
  <site>
    <id>default</id>
    <url>http://updates.jenkins.io/update-center.json</url>
  </site>
</sites>

答案 20 :(得分:0)

为了安全起见,我们不应在实施中使用自签名证书。但是,在开发方面,我们经常必须使用具有自签名证书的试用环境。我试图以编程方式在代码中解决此问题,但失败了。但是,通过将证书添加到jre信任库中解决了我的问题。请找到以下步骤,

  1. 下载站点证书,

  2. 将证书(例如:cert_file.cer)复制到目录$ JAVA_HOME \ Jre \ Lib \ Security

  3. 在Administrator中打开CMD并将目录更改为$ JAVA_HOME \ Jre \ Lib \ Security

  4. 使用以下命令将证书导入信任库

  

keytool-导入-alias ca-文件 cert_file.cer -keystore cacerts   -storepass changeit

如果出现错误,提示无法识别键盘工具,请refer this.

键入,如下所示

  

信任此证书: [是]

  1. 现在尝试使用Java来运行代码或以编程方式访问URL。

更新

如果您的应用服务器是jboss,请尝试在系统属性下方添加

System.setProperty("org.jboss.security.ignoreHttpsHost","true");

希望这会有所帮助!

答案 21 :(得分:0)

由于我有一个QEMU环境,必须在Java中下载文件,所以我想插话。事实证明,QEMU中的import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class Main2Activity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); } } 确实有问题,因为它与主机环境中的/etc/ssl/certs/java/cacerts不匹配。主机环境位于公司代理的后面,因此java cacerts是自定义版本。

如果使用的是QEMU环境,请确保主机系统可以首先访问文件。例如,您可以先在主机上尝试this script来查看。如果脚本在主机上运行正常,但在QEMU中运行不正常,则说明您遇到与我相同的问题。

要解决此问题,我必须对QEMU中的原始文件进行备份,然后将主机环境中的文件复制到QEMU chroot监狱中,然后Java才能在QEMU中正常下载文件。

更好的解决方案是将/etc/ssl/certs/java/cacerts装入QEMU环境;但是我不确定其他文件是否会在此过程中受到影响。因此,我决定使用这种丑陋但简单的解决方法。

答案 22 :(得分:0)

我也有这个问题。

我通过向.keystore添加SSL证书来尝试几乎所有内容,但是,它无法使用Java1_6_x。 对我来说,如果我们开始使用较新版本的Java,Java1_8_x作为JVM,它会有所帮助。

答案 23 :(得分:0)

我的两分钱: 在我的情况下,cacerts不是文件夹,而是文件,并且它存在于两个路径中 发现它后,将.jks文件复制到该文件后,错误消失了。

# locate cacerts    
/usr/java/jdk1.8.0_221-amd64/jre/lib/security/cacerts
/usr/java/jre1.8.0_221-amd64/lib/security/cacerts

备份它们后,我将.jks复制过来。

cp /path_of_jks_file/file.jks /usr/java/jdk1.8.0_221-amd64/jre/lib/security/cacerts
cp /path_of_jks_file/file.jks /usr/java/jre1.8.0_221-amd64/lib/security/cacerts

注意:尽管有file.jks也位于Tomcat的server.xml文件中,但此基本技巧可以解决Genexus项目上的此错误。

答案 24 :(得分:0)

  • 确保您的 JVM 位置。可能有六个 JRE 你的系统。你的Tomcat真正使用的是哪一个?里面的任何地方 在 Tomcat 中运行的代码,编写 println(System.getProperty("java.home"))。注意这个位置。在 /lib/security/cacerts 文件是使用的证书 你的 Tomcat。
  • 找到失败的根证书。这可以是 通过使用 -Djavax.net.debug=all 打开 SSL 调试找到。运行你的 控制台 ssl 中的 app 和 note 记录失败的 CA。它的网址 将可用。就我而言,我惊讶地发现代理 zscaler 失败了,因为它实际上代理了我的调用,并返回了自己的 CA 证书。
  • 在浏览器中粘贴网址。证书将获得 已下载。
  • 使用 keytool import 将此证书导入 cacerts。

答案 25 :(得分:0)

我在 Apache Tomcat/7.0.67 和 Java JVM 版本:1.8.0_66-b18 上也有同样的问题。将 Java 升级到 JRE 1.8.0_241,问题似乎解决了。

答案 26 :(得分:0)

如果您使用的是 JDK 11,则该文件夹中不再包含 JRE。证书的位置是 jdk-11.0.11/lib/security/cacerts。