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

Java 握手完成后,服务器关闭套接字,java,oracle,sockets,ssl,Java,Oracle,Sockets,Ssl,我正在尝试实现一个基本的TLS客户机/服务器框架。在握手之前,一切似乎都很顺利,此时测试echo服务器应该读取并写回它接收到的任何内容 我已经使用-Djavax.net.debug=all从两个方面运行了它,并且协议降级为TLSv1(但从TLSv1.2开始)。这是安装了安全包的GNU/Linux x86-64上的Oracle 8 在服务器使用从SSLSocket.getInputStream()获得的流调用BufferedInputReader.read()之前,任何一方都不会抛出异常。此时,测

我正在尝试实现一个基本的TLS客户机/服务器框架。在握手之前,一切似乎都很顺利,此时测试echo服务器应该读取并写回它接收到的任何内容

我已经使用
-Djavax.net.debug=all
从两个方面运行了它,并且协议降级为TLSv1(但从TLSv1.2开始)。这是安装了安全包的GNU/Linux x86-64上的Oracle 8

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

<>我以前使用GnuTLS实现了C和C++中的TLS 1.2服务器,所以我对所涉及的基本原理有了基本的了解。关于调试这个java实现,我已经通读了诸如和之类的内容,我对问题的描述将遵循这种模式

在服务器端,首先是:

main,读取:TLSv1握手,长度=151
***ClientHello,TLSv1
[...]
[读取]MD5和SHA1哈希:len=151
[...]
%%已初始化:[会话-1,SSL\u NULL\u带\u NULL\u NULL]
匹配别名:tls\u服务器\u密钥
%%谈判:[第1阶段,TLS\U ECDHE\U RSA\U与AES\U 256\U CBC\U SHA]
***你好,TLSv1
发送“RandomCookie”,然后发送服务器证书。此时,客户端已通过:

***ClientHello,TLSv1
[...]
[写入]MD5和SHA1哈希:len=151
main,WRITE:TLSv1握手,长度=151
[...]
main,读取:TLSv1握手,长度=1683
***你好,TLSv1
…读取从服务器发送的相同“随机cookie”,然后

密码套件:TLS\u ECDHE\u RSA\u与\u AES\u 256\u CBC\u SHA
压缩方法:0
扩展重新协商\u信息,重新协商的\u连接:
***
%%已初始化:[会话1,TLS\u ECDHE\u RSA\u与\u AES\u 256\u CBC\u SHA]
**TLS_ECDHE_RSA_与_AES_256_CBC_SHA
[读取]MD5和SHA1哈希:len=81
然后读取服务器证书。客户端拥有CA证书,因此最终会有一个
找到的受信任证书
,然后

***ECDH ServerKeyExchange
服务器密钥:Sun EC公钥,256位
公共x合作社:9933316885058108284849026262573544157293771175927480041267282334641626469072
公共y合作社:815326716015850697355075939588596855714798146675879486894574711221505422
参数:secp256r1[NIST P-256,X9.62 prime256v1](1.2.840.10045.3.1.7)
[读取]MD5和SHA1哈希:len=459
[...]
***海龙石
[读取]MD5和SHA1哈希:len=4
0000:0e00。。。。
***ECDHClientKeyExchange
[...]
[写入]MD5和SHA1哈希:len=70
[...]
主,写入:TLSv1握手,长度=70
[原始写入]:长度=75
[...]
***完成
[...]
main,WRITE:TLSv1握手,长度=48
[原始写入]:长度=53
[...]
主,读取:TLSv1更改密码规范,长度=1
…一些原始读取总计53字节

main,读取:TLSv1握手,长度=48
解密后填充的明文:len=48
[...]
***完成
核实数据:{188,99,75,234,162,27,147,7173,51,170,34}
***
%%缓存的客户端会话:[会话1,TLS\u ECDHE\u RSA\u与\u AES\u 256\u CBC\u SHA]
[读取]MD5和SHA1哈希:len=16
然后报告它已通过此相关代码“连接”:

sock.setUseClientMode(true); 
sock.startHandshake();
in = new BufferedInputStream(sock.getInputStream());
out = new BufferedOutputStream(sock.getOutputStream()); 
服务器设置基本相同,只是套接字来自
SSLServerSocket.accept()
(而不是相应的工厂),并且在握手之前使用
setUseClientMode(false)
。起初,我没有在任何一方使用
setUseClientMode()
,然后注意到javadoc说,“在任何握手发生之前必须调用此方法”,尽管这样做对调试输出或结果没有任何影响

有趣的是,在调用
startHandshake()
并创建in/out BufferedStreams之后,
isClosed()
对客户端和服务器都报告false,但服务器的下一步是在这里(这来自服务器和客户端类都使用的“TLSconnection”类):

其中
isClosed()
现在返回true,这是有意义的,因为服务器上的调试跟踪得出以下结论:

[write]MD5和SHA1散列:len=16
main,WRITE:TLSv1握手,长度=48
[原始写入]:长度=53
%%缓存服务器会话:[会话1,TLS\u ECDHE\u RSA\u与\u AES\u 256\u CBC\u SHA]
main,称为close()
main,称为closeInternal(true)
主,发送TLSv1警报:警告,描述=关闭\u通知
[...]
主,写入:TLSv1警报,长度=32
[...]
main,称为closeSocket(真)
我还使用了一个只写入标准错误的
握手CompletedListener()
来完成这项工作,下面是它对该错误的处理方法:

%%缓存服务器会话:[会话1,TLS\u ECDHE\u RSA\u与\u AES\u 256\u CBC\u SHA]
main,称为close()
main,称为closeInternal(true)
主要的
握手完毕
,发送TLSv1警报:警告,描述=关闭\u通知
在我看来,这似乎是:

  • 客户端连接
  • 服务器接受
  • 发生TLS握手,在此期间:
    • 密码套件已成功协商
    • 客户端验证服务器证书
  • 服务器任意关闭套接字
  • 客户被挂断了
我经历了几个小时又一次的变化:

  • 没有抛出异常
  • 调试输出中没有明显的错误
  • 没有解释为什么服务器会任意关闭套接字
三个类(连接、服务器、客户机)的完整代码约为250行,另外每个测试实例(客户机和服务器)的代码约为100行,因此我没有在这里全部发布,但所显示的是在测试过程中所起的作用
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;
}          
    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.");   
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();
} 
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
        );
    }
}