【问题标题】:How to connect to an HTTPS URL from a JavaFX application如何从 JavaFX 应用程序连接到 HTTPS URL
【发布时间】:2013-11-11 14:54:40
【问题描述】:

我有一个 Javafx 应用程序,它通过 HTTPS 将 GET 和 POST 请求发送到安全的 Web 服务。托管 web 服务的服务器上的 SSL 设置是单向 ssl,即 Javafx 应用程序验证服务器的身份,但服务器不验证胖客户端的身份。

应用程序服务器位于具有证书(由外部机构签署)的 F5 后面。

对于浏览器来说,这不是问题,因为浏览器本身会处理验证服务器的身份并向用户显示相关警告。但是对于胖客户端,我不确定在发送请求之前如何验证服务器的身份。请让我知道如何在 Javafx 应用程序中处理此问题。

我之前确实问过一个与此相关的问题 herehere,但这些都没有帮助。所以,请原谅我对这个话题的了解有限。

任何帮助将不胜感激。

【问题讨论】:

  • 如果服务器证书由已知(到您的 java 版本)CA 和 valid 颁发(它包括各个方面:PKIX 链、不早于 - 不晚于的日期、CN、扩展、等)一切都应该在没有任何额外代码的情况下工作。普通的 HttpsUrlConnection 就可以了。
  • 并且 HttpsUrl 连接会检查所有内容,因此您无法通过 https 协议连接到证书无效的站点。事实上,如果你有旧的 java 版本和旧的根 CA 证书,你甚至无法连接到具有有效证书的站点,而是用新的 CA 颁发的,因为 java 在其内部密钥库中没有它。
  • 如果你想了解更多关于 ssl/https 以及它是如何工作的,我推荐这篇文章zytrax.com/tech/survival/ssl.html
  • 非常感谢user1516873的回复。我希望对具有有效证书的服务器进行这项工作,但不知何故我收到了与 PKIX 相关的异常。所以,我最终忽略了herehere 中描述的所有证书。 :)
  • 当然您可以完全禁用验证,但我强烈建议您不要这样做。当服务器使用自签名或无效证书时,通常会出现与 PKIX 相关的问题。你说的服务器证书是外部权威机构颁发的,你可以用浏览器访问服务器吗?

标签: java ssl https javafx-2 f5


【解决方案1】:

如果您的证书在 Firefox/java 中不起作用,则很可能它的颁发者是 Firefox/java 未知的。

如何让它工作:

  1. 获取服务器的完整证书链。你可以用火狐做到这一点。查看证书 -> 详细信息-> 导出到 .pem 文件。在您的情况下,链将包含至少 2 个证书(cerver 证书和 CA 证书,CA 可能是自签名的,也可能不是)在 .pem 文件中导出 CA 证书。

  2. 现在您可以强制 java 信任该 CA,可以通过多种方式完成,例如,您可以在 jre cacerts 中添加 CA 证书或为 HttpsURLConnection 创建自定义 SSLContext。

  3. 如果您进行 DNS 或 etc.hosts 修改,请将其回滚。连接地址应与证书 CN 匹配,包括通配符。

  4. 使用该代码连接到您的服务器:

    public void test() throws Exception {
        URL u = new URL(
                "https://my-server.com/my-webservices/data");
        HttpsURLConnection http = (HttpsURLConnection) u.openConnection();
        http.setSSLSocketFactory(createSSLContext().getSocketFactory());
        http.setAllowUserInteraction(true);
        http.setRequestMethod("GET");
        http.connect();
    
        InputStream is = http.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder stringBuilder = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null)
        {
            stringBuilder.append(line
                    + "\n");
        }
        System.out.println(stringBuilder.toString());
    
    }
    
    private SSLContext createSSLContext() throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        FileInputStream in = new FileInputStream("path_to_ca_file.pem");
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(null);
        try {
            X509Certificate cacert = (X509Certificate) cf.generateCertificate(in);
            trustStore.setCertificateEntry("ca", cacert);
        } finally {
            IOUtils.closeQuietly(in);
        }
    
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(trustStore);
    
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
        return sslContext;
    }
    

【讨论】:

  • 感谢您的回复user1516873。我试图避免将证书打包到本地,因为当证书过期时,这将需要更新。我认为如果应用程序始终使用服务器上的证书,那将是最好的解决方案。有没有办法做到这一点?
  • 是的,应用程序可以使用它从服务器获得的任何证书,但它只消除了一种攻击 - 嗅探。 MITM en.wikipedia.org/wiki/Man-in-the-middle_attack 和 etc/hosts 重写仍然可以应用于那种 https。
  • 那么这可以通过不像上面代码中那样设置 SSLSocketFactory 来实现吗?谢谢。 http.setSSLSocketFactory(createSSLContext().getSocketFactory());
  • 这取决于您为服务器设置的证书。通常,这可以通过设置全信任 SSL 上下文来实现。您已经知道如何做到这一点,for example 但是从知名机构(VeriSign、GeoTrust 等)获得证书会更好。作为缺点,它会定期花费你一些钱。作为优势,您不必费心更新 CA(java 维护实际的 CA 列表)
  • 谢谢user1516873。你对消除我的困惑有很大的帮助。我希望这个帖子也能对其他用户有价值。
猜你喜欢
  • 2016-01-02
  • 1970-01-01
  • 2019-05-21
  • 2016-03-30
  • 1970-01-01
  • 2013-10-26
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
相关资源
最近更新 更多