PKIX路径构建失败:无法找到请求目标的有效证书路径

时间:2010-10-31 08:09:42

标签: java web-services certificate ssl-certificate

我正在调用以下客户端的一些HTTPS Web服务:

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

/**
 * Handles http and https connections. It sends XML request over http (or https)
 * to SOAP web service and receive the XML reply.
 * 
 * @author mhewedy
 * @date 30.10.2010
 */
public class HttpWSXmlClient
{
    private final String ws_url;
    private byte[] requestData;

    public HttpWSXmlClient(String wsUrl)
    {
        this.ws_url = wsUrl;
    }

    public void readRequest(String xmlRequestFilePath)
    {
        try
        {
            InputStream istream = new FileInputStream(xmlRequestFilePath);
            byte[] data = stream2Bytes(istream);
            istream.close();
            this.requestData = data;
        } catch (Exception e)
        {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 
     * @param ps
     *            PrintStream object to send the debugging info to.
     * @return
     * @throws IOException
     */
    public byte[] sendAndRecieve(PrintStream ps) throws IOException
    {
        if (requestData == null)
            throw new RuntimeException(
                    "the request data didn't initialized yet.");
        if (ps != null)
            ps.println("Request:\n" + new String(requestData));
        URL url = new URL(ws_url);
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        // or HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestProperty("content-type", "text/xml");
        connection.connect();
        OutputStream os = connection.getOutputStream();
        os.write(requestData);
        InputStream is = connection.getInputStream();
        byte[] rply = stream2Bytes(is);
        if (ps != null)
            ps.println("Response:\n" + new String(rply));
        os.close();
        is.close();
        connection.disconnect();
        return rply;
    }

    public byte[] sendAndRecieve() throws IOException
    {
        return sendAndRecieve(null);
    }

    private byte[] stream2Bytes(InputStream istream) throws IOException
    {
        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
        int c;
        while ((c = istream.read()) != -1)
        {
            if (c != 0x0A && c != 0x0D) // prevent new line character from being
            // written
            {
                if (c == 0x09)
                    c = 0x20; // prevent tab character from being written,
                // instead write single space char
                outstream.write(c);
            }
        }
        byte[] ret = outstream.toByteArray();
        outstream.close();
        return ret;
    }

}

测试:

public class Test
{
    private static final String WS_URL = "https://some_server/path/to/ws";

    public static void main(String[] args) throws Exception
    {
        HttpWSXmlClient client = new HttpWSXmlClient(WS_URL);
        client.readRequest("request.xml");
        client.sendAndRecieve(System.out);
    }
}

我得到了以下输出:

Exception in thread "Main Thread" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1591)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1035)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:124)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1096)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1107)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:415)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
    at com.se.swstest.HttpWSXmlClient.sendAndRecieve(HttpWSXmlClient.java:63)
    at com.se.swstest.Test.main(Test.java:11)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:285)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:191)
    at sun.security.validator.Validator.validate(Validator.java:218)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1014)
    ... 12 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:280)
    ... 18 more

我是否需要将任何证书放在jdk / jre / lib / security中? 另外,我有一个xxx_IE.crt和xxx_FX.crt(分别用于Firefox和IE,它们不适用于上述Java客户端,所以我需要Java客户端的特定证书吗?

感谢。

10 个答案:

答案 0 :(得分:23)

您需要设置证书才能点击此网址。使用下面的代码来设置密钥库:

System.setProperty("javax.net.ssl.trustStore","clientTrustStore.key");

System.setProperty("javax.net.ssl.trustStorePassword","qwerty");

答案 1 :(得分:16)

Java 8解决方案:我遇到了这个问题并通过将远程站点的证书添加到我的Java密钥库来解决了这个问题。我的解决方案基于solution at the myshittycode blog,它基于previous solution in mykong's blog。这些博客文章解决方案归结为下载名为InstallCert的程序,该程序是一个Java类,您可以从命令行运行以获取证书。然后,继续在Java的密钥库中安装证书。

InstallCert Readme对我来说非常合适。您只需运行以下命令:

  1. javac InstallCert.java
  2. java InstallCert [host]:[port](在运行命令时输入要在列表中添加的证书的给定列表 number - 可能只是 1
  3. keytool -exportcert -alias [host]-1 -keystore jssecacerts -storepass changeit -file [host].cer
  4. sudo keytool -importcert -alias [host] -keystore [path to system keystore] -storepass changeit -file [host].cer
  5. 如果需要,请参阅引用的README文件以获取示例。

答案 2 :(得分:5)

我遇到过这种情况,这是因为证书链不完整。如果您使用的是标准Java信任库,则可能没有完成证书链所需的证书,而证书链是验证要连接的SSL站点的证书所必需的。

我遇到了一些DigiCert证书的问题,不得不自己手动添加中间证书。

答案 3 :(得分:4)

以下是我用于将网站的公共证书安装到系统密钥库以供使用的解决方案。

使用以下命令下载证书:

unix,linux,mac

openssl s_client -connect [host]:[port|443] < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > [host].crt

<强>窗

openssl s_client -connect [host]:[port|443] < NUL | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > [host].crt

这将创建一个可用于导入密钥库的crt。

使用以下命令安装新证书:

keytool -import -alias "[host]" -keystore [path to keystore] -file [host].crt

这将允许您从导致异常的站点导入新证书。

答案 4 :(得分:4)

当我尝试从Java代码发起SOAP请求时,我遇到了这个问题。对我有用的是:

  1. 通过点击浏览器中的网址获取服务器证书:http://docs.bvstools.com/home/ssl-documentation/exporting-certificate-authorities-cas-from-a-website 此链接包含获取服务器证书的所有步骤

  2. 获得服务器证书后,请按http://java.globinch.com/enterprise-java/security/pkix-path-building-failed-validation-sun-security-validatorexception/#Valid-Certification-Path-to-Requested-Target

  3. 如果此链接死亡,请复制链接中的文本:

      

    修复此错误所需要做的就是添加服务器证书   到您信任的Java密钥库。首先你需要下载   来自服务器的文档。

         

    在硬盘中安装证书后,您可以将其导入   Java信任库。将证书导入受信任的Java   密钥库,你可以使用java'keytool'工具。在命令提示符下   导航到JRE bin文件夹,在我的例子中路径是:C:\ Program   Files \ Java \ jdk1.7.0_75 \ jre \ bin。然后使用keytool命令,如下所示   将证书导入JRE。

         

    keytool -import -alias _alias_name_ -keystore .. \ lib \ security \ cacerts   -file _path_to_cer_file

         

    它会要求输入密码。默认情况下,密码为“changeit”。如果   密码不同,您可能无法导入   证书。

答案 5 :(得分:2)

在Mac OS上,我必须使用系统Keychain Access工具打开服务器的自签名证书,导入它,dobubleclick它然后选择&#34; Always trust&#34; (即使我在导入器中设置相同)。 在此之前,我当然运行java key with -importcert将相同的文件导入到cacert存储。

答案 6 :(得分:2)

如果您不需要SSL安全性,则可能需要将其关闭。

 /**
   * disable SSL
   */
  private void disableSslVerification() {
    try {
      // Create a trust manager that does not validate certificate chains
      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) {
            }
          } };

      // Install the all-trusting trust manager
      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);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (KeyManagementException e) {
      e.printStackTrace();
    }
  }

答案 7 :(得分:1)

如果服务器仅发送其叶子证书而不将构建信任链所需的所有链证书都发送给根CA,也会发生此错误。不幸的是,这是服务器的常见配置错误。

如果大多数浏览器已经从较早的访问中知道丢失的链证书,或者如果叶子证书在 Authority Information Access(AIA)中包含CA颁发者的URL,或者下载丢失的证书,则大多数浏览器都可以解决此问题。 。但是这种行为通常仅限于台式机浏览器,而其他工具则因为无法建立信任链而完全失败。

通过将com.sun.security.enableAIAcaIssuers设置为true,可以使JRE自动下载中间证书

要验证服务器是否正在发送所有链式证书,可以在以下SSL证书验证工具https://www.digicert.com/help/

中输入主机

答案 8 :(得分:0)

对我来说,我在调用webservice调用时遇到了这个错误,请确保该站点有一个有效的ssl,即检查url侧面的徽标,否则需要将证书添加到可信密钥库中你的机器

答案 9 :(得分:0)

我也遇到了这种类型的问题。我正在使用tomcat服务器然后我把tomors的endorsed文件夹然后开始工作。我也用1.7替换了JDK1.6然后它的工作。最后我学习SSL然后我解决了这个问题的类型。首先,您需要从该服务提供商服务器下载证书。然后您握手是成功的。 1.尝试将已签名的文件夹放入您的服务器中 下一步 2.使用jdk1.7

接着 3.尝试使用SSL下载有效证书