我正在尝试实施基本的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实现,我已经阅读了像this和this之类的内容,我对问题的描述将遵循这种模式。
在服务器端,事情始于:
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
对我来说,这似乎是:
我一次又一次地经历过这种变化:
这三个类(连接,服务器,客户端)的完整代码大约是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
);
}
}
答案 0 :(得分:1)
GLabel
返回true。不是同行。结论:你关闭了套接字。
NB Socket.isClosed()
并非必要。您所做的就是断言默认值。必须在握手之前调用它,如果被调用的话。 setUseClientMode()
也是必要的,它是自动的。