Java 6/7中的HTTPURL连接

3

我有一段带有HTTPURL连接的Java代码,它不断报错如下,并指出第47行出现错误。

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

它还在47行显示错误:OutputStream output = httpURLConnection.getOutputStream();

无法追踪url的响应代码,虽然url是活动的可以访问。

这里是堆栈跟踪和Java代码

https://gist.github.com/theakathir/c9138e3ea7470b698ec06a7db7dcdeab

希望有人能帮忙解决这个问题。谢谢。


1
请在此网站上发布代码,而不是仅链接到外部来源。 - home
嗨...这是扩展页面代码。还有堆栈跟踪...这就是为什么我将其包装成链接的原因...如果您无法访问该链接,请告诉我,我可以尝试在此处发布整个代码。 - user7896515
请注意,当SSL/TLS握手失败时(无论是由于任何原因,而不仅仅是证书/PKIX验证),没有HTTP(S)响应代码(也没有任何HTTP(S)响应)。 - dave_thompson_085
我对导入证书进行了一些更改,现在我看到的错误是httpURLConnection字符串:404 java.net.ProtocolException:读取输入后无法写入输出。| 我以为URL是可达的。 - user7896515
3个回答

2
服务器的域名证书通常由一个中间证书颁发机构签署,该机构又由根证书颁发机构签署。所有常见客户端(浏览器- Chrome、Mozilla、Internet Explorer、Safari、Opera 和 Java-VM)都有自己的信任存储库,其中包含安装期间已捆绑的证书颁发机构。
异常意味着您正在指向一个服务器,其服务器证书未被 JVM 信任的证书颁发机构签署。
在这种情况下,通常会构建自己的信任管理器,而不是修改 JVM 的默认信任存储库(位于 $JAVA_HOME\jre\lib\security\cacerts)。
代码片段如下:
package com.example.so.tls;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;


/**
 * <p> https://dev59.com/nKLia4cB1Zd3GeqPm608 </p>
 * @author ravindra
 */
public class TestCustomTruststore {

    public static void main(String[] args) {

        String pathToTruststore = "/path/to/customTruststore.jks"; // truststore has the server certificate loaded
        try {
            File file = new File(pathToTruststore);
            KeyStore trustStore = KeyStore.getInstance("jks");
            trustStore.load(new FileInputStream(file), null);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
            trustManagerFactory.init(trustStore);

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            String urlStr = "https://example.com";
            URL url = new URL(urlStr);
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            httpsURLConnection.setSSLSocketFactory(sslSocketFactory);

            httpsURLConnection.getInputStream();

        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }


    }

}

PS:您需要使用$JAVA_HOME\bin\keytool实用程序创建密钥库并加载证书。


嗨...我需要添加密钥库和加载证书...如果您能分享如何执行的详细步骤,我将不胜感激...谢谢! - user7896515
嗨,我试图利用上面的代码段... 但是在HTTPURLConnection中setSSLSocketFactory显示错误,因为它未定义... 你能指导一下是什么问题吗?谢谢 - user7896515
尝试使用以下命令:keytool -importcert -file <cert_file> -keystore <name_of_truststore> -alias <my_server_cert_domain>。同时,您需要将对象转换为 HttpsURLConnection 而不是 HttpURLConnection,请注意在 'Http' 后面加上 's'。 - Ravindra HV
嗨,我已经下载了证书,但是我怎么知道信任存储和域的名称呢?我在详细信息中找不到它们...请问一下,证书下载后需要注意什么。谢谢。 - user7896515
如果您仍在寻找答案,您需要将证书导入到信任存储中。因此,在创建信任存储时,您基本上提供了信任存储名称!域名与您尝试连接的服务器相同(-别名名称)。 - Ravindra HV
显示剩余2条评论

1

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

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

如果您的证书丢失,您可以使用浏览器下载它,并使用以下命令将其添加到密钥库中:

keytool -import -noprompt -trustcacerts -alias -file -keystore -storepass

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


嗨...谢谢...您能分享一下获取keystore下载链接的适当链接吗?安装必须存储在哪里并运行导入命令? - user7896515
这些“-import”选项中的最后4个需要参数。对于Java7及以上版本,以可导入的形式获取证书的另一种方法是使用“keytool -printcert -sslserver $host[:$port] -rfc”。 - dave_thompson_085
我对导入证书进行了一些更改,现在我看到的错误是httpURLConnection字符串:404 java.net.ProtocolException:无法在读取输入后写入输出。| 我认为URL是可达的.....有什么想法吗? - user7896515

0
一种中间的替代方法是使用您自己的信任存储文件(而不是 JRE/lib/security/cacerts 或官方替代品 jssecacerts),但是您可以通过设置系统属性javax.net.ssl.trustStorejavax.net.ssl.trustStorePassword使默认工厂使用您的文件,而无需编写代码来加载此文件并在SSLSocketFactory中使用它。在许多情况下,这些属性可以在命令行上设置,以便在不进行任何代码更改的情况下运行您的Java程序。
请注意,今天互联网上几乎所有的SSL/TLS证书都不是由根CA(在各种浏览器、客户端和JVM信任存储中)直接颁发的,而是使用中间CA证书,也称为“链”证书,因为它用于从服务器/叶子证书到根证书的链接。偶尔会有两个链证书,原则上甚至可以更多。
验证错误可能不是由于您的JVM缺少根证书,而是由于服务器未能发送链证书;当使用浏览器访问时,您可能不会注意到此错误,因为浏览器有时使用其他获取链证书的方法。在这种情况下,要么手动检查证书链--对于java7及以上版本,您可以使用keytool -printcert -sslserver $host[:$port],如果您有openssl,则使用openssl s_client -connect $host:$port -showcerts,然后在该输出中的每个PEM块上使用openssl x509--或者使用像https://www.ssllabs.com/ssltest/这样的自动测试工具。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接