3

A simple server

listen = getServer();
Logger.getAnonymousLogger().info("Listening to "+listen.toString());
SSLSocket client = (SSLSocket)listen.accept();
// adding this line fixes everything - client.write(42);
client.close();

and a simple client

SocketFactory sockMaker = SSLSocketFactory.getDefault();
Socket server = sockMaker.createSocket("localhost", 1443);
int retval = server.getInputStream().read();
assert retval == -1;
server.close();

If I don't write anything to the SSL Socket, an exception is thrown in the client side:

Exception in thread "main" javax.net.ssl.SSLException:\
  Received close_notify during handshake
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:190)

I don't see why is that. Does the SSL/TLS specification requires you to write things into the socket?

See the full example.

Elazar Leibovich
  • 32,750
  • 33
  • 122
  • 169
  • Your naming convention is a bit confusing regarding what the `client` and `server` sockets are. In addition, the client code is in your `SSLNullServer` directory on GitHub and your server code is in your `SSLNullClient` directory. – Bruno Jun 30 '11 at 15:53
  • @Bruno, your points are valid, my bad. About naming convention, `Socket client` should be `Socket toClient` then it makes sense. – Elazar Leibovich Jun 30 '11 at 16:42

2 Answers2

10

You don't have to write anything into the socket as such, but if you close it straight away, it will generate a close_notify alert (although it's called "alert", it's part of the normal way of closing a TLS/SSL socket).

In addition, SSL/TLS sockets are designed to behave "almost" like normal TCP sockets, but there are details where they don't (and can't) because of the way SSL/TLS works. In particular, at the beginning of an SSL/TLS connection, the SSL/TLS handshake takes place, which involves a number of read/writes from each side, before sending any application data.

The documentation for SSLSocket says:

The initial handshake on this connection can be initiated in one of three ways:

  • calling startHandshake which explicitly begins handshakes, or
  • any attempt to read or write application data on this socket causes an implicit handshake, or
  • a call to getSession tries to set up a session if there is no currently valid session, and an implicit handshake is done.

Essentially, the getInputStream().read() by the client in your example initiates the handshake, which causes the server to proceed with accept() and perform the handshake on its side. However, since you close it (normally, but immediately) on the server side, you don't even let any time for the handshake to complete. Hence the close_notify is sent during the handshake, which causes the exception you get. Had you tried to read or write from the server side, the handshake would have at least completed.

EDIT: Following @EJP's comment, I should clarify what I meant:

  • createSocket("localhost", 1443) on the client side establishes a connection and the server accepts it via accept().
  • getInputStream().read() on the client side makes it initiate the handshake. Thus, it sends a ClientHello TLS message to the server.
  • Because the server uses close() straight after accepting the socket, it sends a close_notify alert. Because the server hasn't started to read/write, it hasn't started the handshake (and thus doesn't complete it).

Note that the purpose of ServerSocket.accept(), which SSLServerSocket implements, is to create an SSLSocket, not necessarily to do anything with it. The SSLServerSocket configures it, but proceeding with the handshake is out of scope. On the one hand, it might sound as making the SSLSocket behave more transparently like a normal TCP socket; on the other hand, it would imply reading from the underlying TCP stream, so it would have side-effects. I haven't tried, but the SSLSocket created by the SSLServerSocket might still be configurable into a client socket. After all as the RFC 2246 glossary says: "client: The application entity that initiates a TLS connection to a server. This may or may not imply that the client initiated the underlying transport connection." This would definitely have consequences regarding the transparency and when to do the handshake from the API point of view.

(Writing an API for SSL/TLS sockets that maps to the API of normal TCP sockets is a tricky exercise, and Java doesn't do too bad a job at it. The real "fun" starts with asynchronous TLS using SSLEngine and NIO channels. It gets even better considering that either side can initiate a new handshake at any time: the implications for the level above are undefined as far as TLS is concerned, which can lead to awkward problems.)

Community
  • 1
  • 1
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • You could look into synchronizing using `addHandshakeCompletedListener` before closing on the server side (not sure if that's the goal of this experiment). At least that could allow you not to send that alert during the handshake. – Bruno Jun 30 '11 at 16:08
  • In a single sentence the problem is, that `.close` does not imply a handshake. It means "close the socket, and if no handshake was done - too bad". I was expecting "close" to imply handshake as well. – Elazar Leibovich Jun 30 '11 at 19:05
  • @Bruno, a simpler fix is to force a handshake in the server side before closing the connection. (I'm trying to debug another exception in `SSLSocket`, and I tried a few simple cases). – Elazar Leibovich Jun 30 '11 at 19:07
  • `close` implies a `close_notify`, whether the handshake has completed or not. I'm not sure why it would make sense for `close` to imply a handshake: that's usually done to start a TLS connection or to re-negotiate some of its parameters; it's not particularly useful when you want to end the connection (which is the purpose of `close`). – Bruno Jun 30 '11 at 19:08
  • @Bruno, I expect `SSLSocket` disallow breaking the protocol. If it is allowed to close a connection before the handshake - I expect `read` to return `-1` when `close_notified` is sent, even without handshake. If it is disallowed to send `close_notify` before handshake, indeed I expect `close` to imply handshake, in order not to break the protocol. – Elazar Leibovich Jun 30 '11 at 19:13
  • Sure, but if you remember my (now deleted) answer to [your previous question](http://stackoverflow.com/questions/6424998/properly-closing-sslsocket) (very similar to what GregS has now said too), you seem to be expecting SSL sockets to behave like normal TCP sockets. They almost do, but they don't: the handshaking/alerting can't always be synchronized in a way that looks natural to a normal read/write at the layer above. The problem you're trying to fix here (closing straight after accepting) is completely artificial anyway: don't expect these edge-cases to behave in a sensible, TCP-like way. – Bruno Jun 30 '11 at 19:16
  • @Bruno let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1027/discussion-between-elazar-leibovich-and-bruno) – Elazar Leibovich Jun 30 '11 at 19:36
  • @Bruno the server had already done the accept() before the handshake starts. The server side is initiated by doing I/O just as the client side is. – user207421 Jun 30 '11 at 23:28
1

The situation is invalid. You are trying to read data that isn't sent. This is an application protocol error. All the statements in Bruno's answer apply as well. The client is trying to do a handshake; the server is trying to close the connection. Arguably the server close could initiate a handshake if it hasn't been done already, but it doesn't.

As you've noted, another workaround would be to call startHandshake() at either end.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    "All the statements in Bruno's answer apply as well.": I've just edited my answer, so I'm not sure if you still agree with it... – Bruno Jul 01 '11 at 00:58
  • @Bruno I agree with it even more now ;-) – user207421 Jul 01 '11 at 04:24
  • First, the situation is definitely valid, since `read` should recognize `EOF`, so it's perfectly OK to do what I did, generally speaking. Second, as I told Bruno. if TLS allows you to send `close_notify` without a handshake, the client's `read` should return `-1`, and shouldn't throw (as protocol don't require an handshake), and if `TLS` require a handshake before `close_notify`, then `close` should complete the handshake before closing. – Elazar Leibovich Jul 01 '11 at 07:52
  • 1
    @Elazar, the semantics of `-1` on the `InputStream` can only become valid once the TLS connection has been established. If this example, you're closing the connection before this happens, so getting an exception should be expected. In addition, as I was saying in an [updated answer to your previous question](http://stackoverflow.com/questions/6424998/properly-closing-sslsocket/6425509#6425509), don't rely too much on reading `-1`: you'll inevitably have to deal with exceptions too. – Bruno Jul 06 '11 at 20:17
  • @Bruno, I perfectly agree with you wrote in the other answer. My problem here is the API, not with the protocol. What I'm saying is, that if you're hiding the handshake from the user - you should not have the abstraction leak this fact to the user as an exception. If the handshake should happen lazily without user's intervention, then you it should either happen upon connect (which makes sense actually), and not on first read, or have it happen automatically when using `close`, or at the very least document that `you must use the SSLSocket before closing it` and throw upon invalid `close`. – Elazar Leibovich Jul 07 '11 at 07:03