握手完成后服务器关闭套接字

时间:2016-04-30 19:16:18

标签: java oracle sockets ssl

我正在尝试实施基本的TLS客户端/服务器框架。在握手之后,一切似乎都很好,此时测试回显服务器应该读取和回写它接收的任何内容。

我从-Djavax.net.debug=all双方运行此协议,协议降级为TLSv1(但是从TLSv1.2开始)。这是安装了安全包的GNU / Linux x86-64上的Oracle 8。

在服务器使用从BufferedInputReader.read()获取的流调用SSLSocket.getInputStream()之前,不会抛出任何异常。此时,测试客户端正在等待控制台输入发送到回显测试服务器。

我之前使用GnuTLS在C和C ++中实现了TLS 1.2服务器,因此我对所涉及的基础知识有了基本的了解。关于调试这个java实现,我已经阅读了像thisthis之类的内容,我对问题的描述将遵循这种模式。

在服务器端,事情始于:

main, READ: TLSv1 Handshake, length = 151
*** ClientHello, TLSv1
[...]
[read] MD5 and SHA1 hashes:  len = 151
[...]
%% Initialized:  [Session-1, SSL_NULL_WITH_NULL_NULL]
matching alias: tls_server_key
%% Negotiating:  [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
*** ServerHello, TLSv1

A" RandomCookie"发送,然后服务器证书。此时客户已经完成了:

*** ClientHello, TLSv1
[...]
[write] MD5 and SHA1 hashes:  len = 151
main, WRITE: TLSv1 Handshake, length = 151
[...]
main, READ: TLSv1 Handshake, length = 1683
*** ServerHello, TLSv1

...读取相同的" RandomCookie"从服务器发送,然后......

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized:  [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
** TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
[read] MD5 and SHA1 hashes:  len = 81

然后读取服务器证书。客户端具有签名的CA证书,因此最终会有Found trusted certificate,然后

*** ECDH ServerKeyExchange
Server key: Sun EC public key, 256 bits
  public x coord: 99333168850581082484902625735441572937781175927498004126728233464162626469072
  public y coord: 8153267160158506973550759395885968557147147981466758879486894574711221505422
  parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
[read] MD5 and SHA1 hashes:  len = 459
[...]
*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00                                        ....
*** ECDHClientKeyExchange
[...]
[write] MD5 and SHA1 hashes:  len = 70
[...]
main, WRITE: TLSv1 Handshake, length = 70
[Raw write]: length = 75
[...]
*** Finished
[...]
main, WRITE: TLSv1 Handshake, length = 48
[Raw write]: length = 53
[...]
main, READ: TLSv1 Change Cipher Spec, length = 1

......一些原始读取总计53个字节......

main, READ: TLSv1 Handshake, length = 48
Padded plaintext after DECRYPTION:  len = 48
[...]
*** Finished
verify_data:  { 188, 99, 75, 234, 162, 27, 147, 7, 173, 51, 170, 34 }
***
%% Cached client session: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
[read] MD5 and SHA1 hashes:  len = 16

然后报告已经通过此相关代码进行了连接&#34;

sock.setUseClientMode(true); 
sock.startHandshake();
in = new BufferedInputStream(sock.getInputStream());
out = new BufferedOutputStream(sock.getOutputStream()); 

服务器设置大致相同,但套接字来自SSLServerSocket.accept()(而不是适当的工厂),并且在握手之前使用setUseClientMode(false)。最初我根本没有使用setUseClientMode(),然后注意到javadoc说,&#34;必须在任何握手发生之前调用此方法&#34;尽管这样做根本没有任何区别调试输出或结果。

有趣的是,在调用startHandshake()并创建输入/输出BufferedStream之后,isClosed()报告客户端和服务器都为false,但是服务器的下一步是在这里(这是来自服务器和客户端类使用的&#34; TLSconnection&#34;类):

public int recv (byte[] data) throws IOException
{
    if (sock.isClosed()) return -1;
    int len = in.read(data, 0, data.length);
    if (len == -1) shut();
    return len;
}          

isClosed()现在返回true,这是有道理的,因为服务器上的调试跟踪结束了:

[write] MD5 and SHA1 hashes:  len = 16
main, WRITE: TLSv1 Handshake, length = 48
[Raw write]: length = 53
%% Cached server session: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
main, called close()
main, called closeInternal(true)
main, SEND TLSv1 ALERT:  warning, description = close_notify
[...]
main, WRITE: TLSv1 Alert, length = 32
[...]
main, called closeSocket(true)

我也使用handshakeCompletedListener()完成此操作,只需写入标准错误,以及它对此的作用:

%% Cached server session: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
main, called close()
main, called closeInternal(true)
main

HANDSHAKE COMPLETED

, SEND TLSv1 ALERT:  warning, description = close_notify

对我来说,这似乎是:

  • 客户端连接。
  • 服务器接受。
  • 发生TLS握手,在此期间:
    • 密码套件已成功协商。
    • 客户端验证服务器证书
  • 服务器随意关闭套接字。
  • 客户端被搁置。

我一次又一次地经历过这种变化:

  • 没有抛出异常。
  • 调试输出中没有明显的错误。
  • 没有解释服务器为什么随意关闭套接字。

这三个类(连接,服务器,客户端)的完整代码大约是250行,另外每个测试实例(客户端和服务器)大约有100行,所以我还没有在这里发布它,但是显示的内容是调试会话期间播放的内容。

我认为这可能与证书或密钥错误无关,因为没有任何迹象表明这种情况,或者握手失败,或者任何人因任何原因拒绝任何其他人。< / p>

回应Steffen Ullrich的评论&#34;看起来服务器正在进行干净关机(发送close_notify),所以我建议你的代码中的某些东西实际上是在明确关闭&#34; - 这与我首先想到的相同 - 这是测试服务器代码中导致sock.isClosed()返回true的部分:

    while (true) {
        TLSconnection con = null;
        try { con = server.acceptConnection(true); }
        catch (Throwable ex) {
            ex.printStackTrace();
            continue;
        }

        if (con != null) {
            System.out.println (
                "Accepted: "
                +TestCommon.describeConnection(con.getDetails())
            );
        }

        while (con != null) {
        // Echo.
            try {
                int check = con.recv(buffer);
                if (check == -1) {
                    System.out.println("Disconnected.");   

与此相关的字面意思是&#34;接受&#34;打印出来的; describeConnection()方法很简单:

static String describeConnection (TLSconnection.Details details)
{
    if (details.addr == null) return "Not connected.";
    StringBuffer sb = new StringBuffer(details.addr.getHostAddress()+":")
        .append(details.remotePort).append(" (local ")
        .append(details.localPort).append(")");
    return sb.toString();
} 

然后con.recv()方法是完全显示的方法,其中isClosed()现在返回true。同样,在临时中没有出现nullPointerExceptions等等。换句话说,在接受和recv之间的某个地方,JVM正在关闭套接字。

acceptConnection()方法(TLSserver)是:

public TLSconnection acceptConnection (boolean handshake)
throws TLSexception {
    try (SSLSocket client = (SSLSocket)listenSock.accept()) {
        client.setUseClientMode(false);
        return new TLSconnection(client, handshake);
    } catch (Throwable ex) {
        throw new TLSexception (
            "Accept failed:",
            ex
        );
    }
}       

1 个答案:

答案 0 :(得分:1)

如果关闭套接字,则

GLabel返回true。不是同行。结论:你关闭了套接字。

NB Socket.isClosed()并非必要。您所做的就是断言默认值。必须在握手之前调用它,如果被调用的话。 setUseClientMode()也是必要的,它是自动的。