Java https 在握手时遇到问题

Posted

技术标签:

【中文标题】Java https 在握手时遇到问题【英文标题】:Java https having problems with handshaking 【发布时间】:2012-05-29 01:22:33 【问题描述】:

我正在用 java 编写一个 https 服务器,它将接受和响应 ajax 请求。我有这一切使用http连接,但https被证明难以设置。如果我使用 openssl,我可以访问服务器并得到预期的响应:openssl s_client -connect localhost:5001 但是来自浏览器的 ajax 调用失败。我不知道从这里去哪里。

这是尝试 ajax 调用后来自服务器的堆栈跟踪:

javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.writeRecord(Unknown Source)
at sun.security.ssl.AppOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.io.BufferedWriter.flush(Unknown Source)
at com.myDomain.HttpsServer.main(HttpsServer.java:223)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.read(Unknown Source)
... 11 more

ajax 调用:

var command, req;
  command = 
    command: "getStatus",
  ;
  command = JSON.stringify(command);
  req = $.ajax("https://localhost:5001", 
    data: command,
    dataType: "jsonp",
    timeout: 1000
  );

Java 服务器:

private static SSLServerSocket createSSLSocket()
    SSLServerSocketFactory sslServerSocketFactory =
        (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
    try
      SSLServerSocket sslSocket =
        (SSLServerSocket) sslServerSocketFactory.createServerSocket(port, 10, InetAddress.getByName("127.0.0.1"));
      return sslSocket;
     catch (Exception e)
        e.printStackTrace();
    
    return null;


public static void main (String args[]) throws Exception 
    SSLServerSocket sslSocket;
    sslSocket = createSSLSocket();

    while(running) 
        SSLSocket connected = (SSLSocket) sslSocket.accept();
        try
         BufferedWriter w = new BufferedWriter(
            new outputStreamWriter(connected.getOutputStream()));
             BufferedReader r = new BufferedReader(
                new InputStreamReader(connected.getInputStream()));
             w.write("HTTP/1.0 200 OK");
             w.write("foo");
             w.newLine();
             w.flush();  //THIS IS WHERE THE ACTUAL EXCEPTION IS THROWN (LINE 223)
             w.close();
             r.close();
             connected.close();
         catch (Exception e)
         e.printStackTrace();
        
    

这正在运行:

java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=123456 myApp

当使用调试选项完成时,看起来 mySrvKeystore 正在被正确使用。

更新

以下是调试输出的更多信息:

*** ECDH ServerKeyExchange
 Server key: Sun EC public key, 256 bits
   public x coord:      59120686551233854673577061225846672012454441193286172303206804252170042475984
   public y coord: 64356797475544123011351526783519675095229374542555548418334080869325161950574
   parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
 *** ServerHelloDone
 main, WRITE: TLSv1 Handshake, length = 1317
 main, received EOFException: error
 main, handling exception: javax.net.ssl.SSLHandshakeException: Remote host closed connection      during handshake
 %% Invalidated:  [Session-1, TLS_ECDHE_RSA_WITH_RC4_128_SHA]
 main, SEND TLSv1 ALERT:  fatal, description = handshake_failure
 main, WRITE: TLSv1 Alert, length = 2
 main, called closeSocket()

【问题讨论】:

在所有浏览器中都失败了吗?有一个相同stacktrace的问题:***.com/questions/4827190/… 你有这个问题吗?谢谢 看起来这是 chrome 特有的问题。看看这个人:***.com/questions/7535154/… 【参考方案1】:

像这样确定握手失败可能会很棘手。尝试使用以下命令打开 SSL/TLS 调试:

-Djavax.net.debug=true

您可能还想在服务器上添加信任库:

-Djavax.net.ssl.trustStore=mySrvKeystore

您使用的浏览器可能不支持密码套件TLS_ECDHE_RSA_WITH_RC4_128_SHA。可能是客户端/服务器尝试进行多次协商,但最后一次失败。您可以使用以下命令在服务器上打印出启用的密码套件:

String[] cipherSuites = sslSocket.getEnabledCipherSuites();
for (int x = 0; x < cipherSuites.length; x++) 
   System.out.println(cipherSuites[x]);

您还可以考虑使用sslSocket.setEnabledCipherSuites() 限制服务器提供的密码套件。

请参阅 Using AES in JSSE 了解 Sun 的一些旧示例。

一般来说,现代浏览器可以连接密码套件,如 TLS_RSA_WITH_AES_128_CBC_SHA

【讨论】:

以上是关于Java https 在握手时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

centos 7 Nginx ssl证书配置

WebSocket协议 学习笔记

WebSocket协议 学习笔记

Node JS 推送服务器发送/接收

无法注册车把助手

为啥 java 在 SSL 握手期间不发送客户端证书?