bc-java icon indicating copy to clipboard operation
bc-java copied to clipboard

createSocket with consumed InputStream and com.sun.net.ssl.requireCloseNotify throws SocketException

Open ulrokx opened this issue 4 months ago • 1 comments

When the createSocket factory method is used with a non-null InputStream consumed argument, a SequenceInputStream is created. One consideration with using SequenceInputStream is the following

After each input stream from the enumeration is exhausted, it is closed by calling its close method.

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/SequenceInputStream.html

If a client opens a connection, handshakes successfully, writes some data, then closes the connection without sending close_notify, this will typically result in a TlsNoCloseNotifyException being thrown. If the com.sun.net.ssl.requireCloseNotify is set to false, then instead we send our own close_notify. If this property is used in combination with the InputStream consumed argument, BC will try to write to a closed socket and throw a SocketException

java.net.SocketException: Socket closed
sun.nio.ch.NioSocketImpl.ensureOpenAndConnected (NioSocketImpl.java:165)
sun.nio.ch.NioSocketImpl.beginWrite (NioSocketImpl.java:371)
sun.nio.ch.NioSocketImpl.implWrite (NioSocketImpl.java:416)
sun.nio.ch.NioSocketImpl.write (NioSocketImpl.java:445)
sun.nio.ch.NioSocketImpl$2.write (NioSocketImpl.java:831)
java.net.Socket$SocketOutputStream.write (Socket.java:1035)
org.bouncycastle.tls.RecordStream.writeRecord (RecordStream.java:309)
org.bouncycastle.tls.TlsProtocol.safeWriteRecord (TlsProtocol.java:950)
org.bouncycastle.tls.TlsProtocol.raiseAlertWarning (TlsProtocol.java:1721)
org.bouncycastle.tls.TlsProtocol.handleClose (TlsProtocol.java:300)
org.bouncycastle.tls.TlsProtocol.safeReadRecord (TlsProtocol.java:891)
org.bouncycastle.tls.TlsProtocol.readApplicationData (TlsProtocol.java:842)
org.bouncycastle.jsse.provider.ProvSSLSocketWrap$AppDataInput.read (ProvSSLSocketWrap.java:801)
java.io.SequenceInputStream.read (SequenceInputStream.java:198)
java.io.BufferedInputStream.fill (BufferedInputStream.java:244)
java.io.BufferedInputStream.read1 (BufferedInputStream.java:284)
java.io.BufferedInputStream.read (BufferedInputStream.java:343)

ulrokx avatar Aug 06 '25 21:08 ulrokx

I don't think the SequenceInputStream is relevant here.

I see: client connects, handshakes, and then at some point fails to read next application data record -> server has closed the connection, in this case without sending close_notify alert. Since client has configured to not requireCloseNotify it ignores this and continues to handleClose.

Usual close handling includes trying to send a close_notify alert ourselves, which often fails if the socket is fully closed.

So the issue seems to be that a SocketException when trying to send our close_notify should probably be caught and ignored (at least in the reactive case, perhaps always).

Possibly we should not even try to send the close_notify if we are reacting to a peer closure that didn't send one, but it seems better to try, so long as the exception is ignored.

peterdettman avatar Aug 07 '25 04:08 peterdettman