来自 Java 客户端的 SSL 错误,但在 Firefox 中以 POSTER 形式工作

Posted

技术标签:

【中文标题】来自 Java 客户端的 SSL 错误,但在 Firefox 中以 POSTER 形式工作【英文标题】:SSL error from Java client but works form POSTER in Firefox 【发布时间】:2014-10-22 14:40:54 【问题描述】:

我只是在我的服务器上设置了 SSL 证书。我很确定他们设置正确。在浏览器中转到https://mydomain.com/myapp 时,页面正确加载,地址栏中显示绿色锁。

从 Firefox 发布 POST>POSTER 到这个 HTTPS url 我得到一个有效的响应。

如果我从我的 Java 客户端执行相同的 POST,我会收到以下错误:

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

在我的服务器上,我已将 CA_ROOT 证书放在 JAVA.../jre/lib/security/cacert 密钥库中。

这是我从 Java 客户端发布的代码。

URL url = new URL(Global.SERVER_URL);
HttpsURLConnection connection = null;
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setReadTimeout(45000);
connection.setRequestProperty("Content-Type", "text/json");
connection.connect();

请注意:这不是自签名证书。它是由 CA 颁发的

我感觉 Glassfish 没有发送完整的证书链。我查看了浏览器获得的证书,它是完整的证书链。我查看了 SSL 错误,那只是中间证书和我的域。

如何让 Glassfish 发送完整的证书链?

检查 SSL 链

openssl.exe s_client -connect mydomain.com:443

退货

WARNING: can't open config file: /usr/local/ssl/openssl.cnf
Loading 'screen' into random state - done
CONNECTED(00000190)
depth=0 C = US, ST = <edited>, L = <edited>, O = <edited>, OU = <edited>, CN = <edited>
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = US, ST = <edited>, L = <edited>, O = <edited>, OU = <edited>, CN = <edited>
verify error:num=27:certificate not trusted
verify return:1
depth=0 C = US, ST = <edited>, L = <edited>, O = <edited>, OU = <edited>, CN = <edited>
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/C=US/ST=<edited>/L=<edited>/O=<edited>/OU=<edited>/CN=<edited>
   i:/O=Cybertrust Inc/CN=Cybertrust Public SureServer SV CA
---
Server certificate
-----BEGIN CERTIFICATE-----
<edited>
-----END CERTIFICATE-----
subject=/C=US/ST=<edited>/L=<edited>/O=<edited>/OU=<edited>/CN=<edited>
issuer=/O=Cybertrust Inc/CN=Cybertrust Public SureServer SV CA
---
No client certificate CA names sent
---
SSL handshake has read 1676 bytes and written 513 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: <edited>

    Session-ID-ctx:
    Master-Key: <edited>
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1410274974
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---
read:errno=0

解决方案

接受 Bruno 对概念的回答

其他细节:

    获取密钥库资源管理器。它是浏览密钥库的绝佳 GUI 工具。 使用 Keystore Explorer 打开 keystore.jks。 右键单击有问题的证书 (mydomain) 并查看详细信息>证书链详细信息。 如果它没有显示完整的证书链,则通过右键单击证书Edit Certificate Chain>Append Certificate添加它。 重新启动 Glassfish。

【问题讨论】:

它是自签名证书吗? @bhowden 它是由我的公司发行的。 CA 是巴尔的摩网络信任根。不确定默认情况下是否在 jvm 信任中。在服务器上,我将它添加到 jvm 信任中 好的。运行 keytool -list -keystore $JRE_HOME\lib\security\cacerts 时是否列出了证书? 您使用的是哪个版本的 Java?你能用openssl s_client -servername your.host.name -connect your.host.name 443检查你得到的链条吗?他们有秩序吗? @codeNinja 在我看来,它只返回一个证书,而不是完整的链,证书链中位置 0 只有一个条目。 【参考方案1】:
---
Certificate chain
 0 s:/C=US/ST=<edited>/L=<edited>/O=<edited>/OU=<edited>/CN=<edited>
   i:/O=Cybertrust Inc/CN=Cybertrust Public SureServer SV CA
---

与您在评论中所说的不同,根据此输出,您的服务器没有发送任何中间证书。证书链只有一个证书(位置 0):服务器证书。

在我的服务器上,我已将 CA_ROOT 证书放在 JAVA.../jre/lib/security/cacert 密钥库中。

在服务器端的信任库中添加中间证书不会对服务器提供的链产生影响(它仅用于验证客户端,如果适用的话)。

您需要确保按照this question 中所述的完整链设置您的密钥库条目,并遵循与this answer 相同的过程。

您在浏览器中看到的很可能是它重构的链,浏览器也可能知道这些中间 CA 证书,而 JRE 可能不知道(使用了不同的信任锚集):您只会增加默认 JRE 通过正确显示完整链来接受您的服务器证书的机会。 (请注意,您不需要在链中提供根 CA 证书本身,它不会造成伤害,但这只是网络开销。)

【讨论】:

感谢您的帮助。这做到了。我下载了 Keystore Explorer 并简单地双击了我的 Keystore。然后我右键单击有问题的 CERT 并添加 ROOT 和中间证书。然后重启 Glassfish!【参考方案2】:

我认为您讨论的问题与我在这里回答的问题相同:

https://***.com/a/25734956/1083338

【讨论】:

是的。但我不想关闭 SSL 验证。更好的解决方案是在运行时将所有证书发送到 TrustStore。【参考方案3】:

Java 密钥库是 cacerts,但不是 cacert

【讨论】:

【参考方案4】:

SSLHandshakeException:

当一个失败的连接看起来像这样时出现。通常表明存在某种类型的证书验证问题,很可能您的 Truststore 不包含它需要的受信任的根证书。并且 表示客户端和服务器无法协商所需的安全级别。连接不再可用。

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

使用以下代码发送 HTTP POST 请求的详细示例。

private void sendPost() throws Exception 

        String url = "https://selfsolve.apple.com/wcResults.do";
        URL obj = new URL(url);
        HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

        //add reuqest header
        con.setRequestMethod("POST");
        con.setRequestProperty("User-Agent", "Mozilla/5.0");
        con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
        con.setRequestProperty("Content-Type", "text/json");

        String urlParameters = "sn=C02G8416DRJM&cn=&locale=&caller=&num=12345";

        // Send post request
        con.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.writeBytes(urlParameters);
        wr.flush();
        wr.close();

        int responseCode = con.getResponseCode();
        System.out.println("\nSending 'POST' request to URL : " + url);
        System.out.println("Post parameters : " + urlParameters);
        System.out.println("Response Code : " + responseCode);

        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) 
            response.append(inputLine);
        
        in.close();

        //print result
        System.out.println(response.toString());

    

如果响应码是 200,表示看起来不错。 有关解决相同异常的更多详细信息,请参阅以下链接。

How to solve javax.net.ssl.SSLHandshakeException Error? Test trusted certificate Test a Java SSL connection

【讨论】:

我了解帖子部分的工作原理。我也确信该错误是由于 Brower 在其 TrustStore 中有 CERT 而 Java 没有。问题是我如何为部署我的产品的每个人解决这个问题? @codeNinja 你只需要调试它,还需要确认证书。但是为什么要使用 java 代码进行测试。它可以在浏览器上运行。 HTTP 请求实际上是在 JAVA 应用程序中发出的。我只是在浏览器中进行测试以进行调试。【参考方案5】:

浏览器和 Java 使用不同的可信根证书集,默认情况下浏览器从操作系统获取此信息,Java 支持自己的列表,这就是为什么它在浏览器中可能是绿色的,而在 Java 中不支持

要检查哪些证书支持您的 Java 版本: &lt;jre_path&gt;\bin\keytool -keystore "&lt;jre_path&gt;\lib\security\cacerts" -storepass changeit -list

如果您没有看到您的证书,只需将其添加到 &lt;jre_path&gt;\lib\security\cacerts 文件中。

【讨论】:

它不是自签名证书。它是由 CA 颁发的。如问题中所述,如果通过浏览器完成请求/响应(地址栏中的绿色锁和所有内容),则请求/响应可以完美地工作 @codeNinja 正如 terma 所说,您的浏览器和 Java 具有独立的可信 CA 集。 Java 的可信 CA 根集可能不包括您选择的 CA 的根证书。 这是一个 Java Web Start 应用程序,部署给公司的许多用户。将证书添加到每个人的计算机是不可行的。

以上是关于来自 Java 客户端的 SSL 错误,但在 Firefox 中以 POSTER 形式工作的主要内容,如果未能解决你的问题,请参考以下文章

java SSL服务器和客户端的Java示例,以及如何生成密钥库

来自环境变量的钱包位置,而不是 oci 客户端的 sqlnet.ora

如何修复来自 Apollo 客户端的格式错误的身份验证标头错误

IBM Worklight 6.1 [收到来自客户端的错误令牌]

ActiveMQ配置ssl安全连接

来自客户端的 Java (Tomcat) websocket 参数