PKIX路径不与Windows环境中的任何信任锚错误链接

时间:2014-05-21 06:02:48

标签: ssl java-7 keystore keytool

我对SSL和Webservices如何在细粒度级别上工作有点痴迷。我正在开发一个调用多个Web服务的系统,其中一些具有安全URL,另一些则没有什么问题。但是,目前我正在与Endicia的LabelServer Web API进行集成。网络服务用于计算和打印邮资。

测试URL和WSDL位于:https://www.envmgr.com/LabelService/EwsLabelService.asmx

我使用wsimport创建和设置Java客户端以连接到此Web服务,但是当我尝试所有这些时,我收到错误

  

PKIX路径验证失败:java.security.cert.CertPathValidatorException:Path不与任何信任锚链接

此错误记录在此处:Java7 Refusing to trust certificate in trust store

其中讨论了Java 7如何使用带有“坏”关键字的自签名证书强制出错。在这种情况下,错误定义为不包含keyCertSign。 Web服务确实可以使用Java 6.我可以相信这种情况可能适用于此证书,因为它仅用作测试服务器,但我不知道如何验证。

有关于它的错误报告已经解决(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7018897),但我不确定如何解决这个问题转换为修复Windows Tomcat环境的问题。我将证书导出到我的机器上,但不确定如何从那里开始。

编辑: 我尝试使用OpenSSL修改证书并将其添加到我的密钥库,如“拒绝信任信任存储中的证书”链接中所述,它不起作用。看起来这是一个由证书所有者完成的过程,对吗?我想知道是否有一些方法可以配置我的Java 7环境以通过此证书。

5 个答案:

答案 0 :(得分:11)

默认的Java证书检查非常严格,并且显然变得更加严格。一种解决方法是使用自定义SSLContext初始化X509TrustManager。我曾经为测试写过的无所事事,即完全不安全的信任经理看起来像这样:

TrustManager[] trustAllCerts = new TrustManager[]{
   new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers()
         {
            return null;
         }
      public void checkClientTrusted(
         java.security.cert.X509Certificate[] certs,
         String authType )
         {
         }
      public void checkServerTrusted(
         java.security.cert.X509Certificate[] certs,
         String authType )
         {
         }
   }
};

显然,您希望在实际程序中实际检查证书链。然后,您可以尝试使用它初始化SSLContext,如果您的API没有其他配置SSL的方法,请致电SSLContext.setDefault()。如果API使用默认的SSL上下文,那么这应该可行。

在这种情况下,密钥用法似乎不是问题,因为证书链不是自签名的。测试URL似乎表明叶证书不是自签名的,(2)链中的其他两个证书似乎已启用证书签名。另一种可能性是Java 6和Java 7具有单独的信任存储,并且根证书不在Java 7存储中。您可能需要仔细检查。如果您有权访问OpenSSL,则可以从服务器获取证书链:

openssl s_client -host www.example.com -port 443 -showcerts

显然更新信任存储是关键(双关语)。 OP报告:

我下载了OpenSSL for Windows 64,然后使用此命令下载证书链:

openssl s_client -host www.webserviceurl.com -port 443 -showcerts > c:\temp\certchain_output.crt

然后我想将它导入我的浏览器的密钥库(从JDK的主目录/ jre / lib / security):

keytool -import -alias ca -file certchain_output.crt -keystore cacerts -storepass changeit

我相信使用X509TrustManager也可以提供有效的解决方案。

答案 1 :(得分:7)

证书链验证逻辑确实在Java 6和Java 7之间发生了变化。似乎证书链被认为是无效的,因为中间证书的有效期结束日期是根证书的有效期结束日期之后。这是使用JDK 1.7.0_72和1.8.0_25修复的a Java bug

如果您无法升级JDK,那么这是一种解决方法,因为您说在调试环境中并且您可以控制您的密钥库。您当然不能更改任何证书(因为您没有私钥),但您可以将中间证书或服务器证书导入本地密钥库,这意味着默认情况下它将受信任,其余的链条验证没有任何问题。

  1. www.envmgr.comwww.starfieldtech.com的证书下载为 trusted_certificate.pem
  2. 使用keytool将证书导入名为 my_keystore 的本地密钥库keytool -keystore my_keystore -importcert -file trusted_certificate.pem
  3. 启动wsimport -J-Djavax.net.ssl.trustStore=my_keystore https://www.envmgr.com/LabelService/EwsLabelService.asmx

答案 2 :(得分:2)

尝试使用keytool将您需要的证书导入位于jdk_xxx / jre / lib / security / cacerts的java信任库中

设置jvm参数Djavax.net.debug = ssl以查看更多调试信息

答案 3 :(得分:1)

Rhashimoto帮助我找到了对我有用的解决方案:

我下载了OpenSSL for Windows 64,然后使用此命令下载证书链:

openssl s_client -host www.webserviceurl.com -port 443 -showcerts > c:\temp\certchain_output.crt

然后我想将它导入我的浏览器的密钥库(从JDK的主目录/ jre / lib / security):

keytool -import -alias ca -file certchain_output.crt -keystore cacerts -storepass changeit

我相信使用X509TrustManager也可以提供有效的解决方案。

答案 4 :(得分:0)

尝试此OUT,它接受所有

TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
            public void checkClientTrusted(X509Certificate[] certs, String authType) { }
            public void checkServerTrusted(X509Certificate[] certs, String authType) { }

        } };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) { return true; }
        };
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);