Hi Xuelei, I’ve attached a java file with a method that can be used as a test case. The caller will need to provide SSLEngines. I assume that is something you can do? As I said this is with TLS 1.2.
I catch the exceptions I mentioned in my last email. Although there are comments that I do not think that these exceptions make sense. Additionally, the failing assertions are currently wrapped in if(false) {} so that the method will complete. Let me know if you have any questions. - Tim
JDKSSLEngineTest.java
Description: Binary data
> On Aug 3, 2018, at 6:47 PM, Xuelei Fan <xuelei....@oracle.com> wrote: > > Hi Tim, > > Your concerns make sense to me. Did you have a test case that I can use to > reproduce the issues? > > Thanks, > Xuelei > > On 8/3/2018 5:04 PM, Tim Brooks wrote: >> Hi Xuelei >> I pulled the JDK 11 source today and applied your patch. And then I built >> the jdk to run some tests. I hope that is the correct approach. >> It appears that this change resolved some of my prior issues. But I notice >> some other issues. This is tested with TLS 1.2. I have not really setup my >> environment yet for TLS 1.3, so I was not able to test that code path. I >> have -Djavax.net.debug=all enabled. However, I have pruned some of messages >> from this email to reduce text. If you need more let me know. >> Client: >> 1. ClientHello.java:633|Produced ClientHello handshake message >> 2. SSLEngineOutputRecord.java:465|WRITE: TLS12 handshake, length = 204 >> Server: >> 3. SSLEngineInputRecord.java:214|READ: TLSv1.2 handshake, length = 204 >> 4. ClientHello.java:788|Consuming ClientHello handshake message >> 5. ClientHello.java:818|Negotiated protocol version: TLSv1.2 >> 6. ServerHello.java:361|Produced ServerHello handshake message >> 7. CertificateMessage.java:263|Produced server Certificate handshake message >> 8. ServerHelloDone.java:97|Produced ServerHelloDone handshake message >> 9. SSLEngineOutputRecord.java:465|WRITE: TLS12 handshake, length = 1086 >> Client: >> 10. SSLEngineInputRecord.java:214|READ: TLSv1.2 handshake, length = 1086 >> 11. ServerHello.java:862|Consuming ServerHello handshake message >> 12. ServerHello.java:958|Negotiated protocol version: TLSv1.2 >> 13. CertificateMessage.java:358|Consuming server Certificate handshake >> message >> 14. ServerHelloDone.java:142|Consuming ServerHelloDone handshake message >> 15. RSAClientKeyExchange.java:195|Produced RSA ClientKeyExchange handshake >> message >> 16. ChangeCipherSpec.java:109|Produced ChangeCipherSpec message >> 17. ChangeCipherSpec.java:109|Produced ChangeCipherSpec message >> 18. SSLEngineOutputRecord.java:465|WRITE: TLS12 handshake, length = 262 >> 19. SSLEngineOutputRecord.java:465|WRITE: TLS12 change_cipher_spec, length = >> 1 >> 20. SSLEngineOutputRecord.java:465|WRITE: TLS12 handshake, length = 16 >> At this point I do not send that data yet to the server. The client has >> produced the client key exchange and client finished messages. But they are >> still in transit (in this scenario). >> Server (we manually call closeOutboud() because we have decided we need to >> close this socket): >> 21. SSLEngineImpl.java:745|Closing outbound of SSLEngine >> 22. SSLEngineOutputRecord.java:465|WRITE: TLS12 alert, length = 2 >> 23. SSLEngineOutputRecord.java:465|Raw write (0000: 15 03 03 00 02 01 5A >> ......Z) >> - I believe this is user_canceled alert >> 24. SSLEngineOutputRecord.java:465|Raw write (0000: 15 03 03 00 02 01 00 >> .......) >> - I believe this is close_notify >> At this point: >> Server engine.isOutboundDone() = true >> Server engine.isInboundDone() = false >> Client: >> 24. SSLEngineInputRecord.java:214|READ: TLSv1.2 alert, length = 2 >> 25. Alert.java:232|Received alert message ("Alert": {"level" : "warning", >> "description": "user_canceled"}) >> 26. SSLEngineInputRecord.java:214|READ: TLSv1.2 alert, length = 2 >> 27. Alert.java:232|Received alert message ("Alert": {"level": "warning", >> "description": "close_notify"}) >> 28. Fatal (UNEXPECTED_MESSAGE): Received close_notify during handshake ( >> "throwable" : { >> javax.net.ssl.SSLProtocolException: Received close_notify during handshake >> at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:126) >> at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) >> at >> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:307) >> at >> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:263) >> At this point I want to note that it feels weird that this produces an >> exception. The other side said "user_canceled" and then sent "close_notify". >> That seems correct to me? So it feels weird that consumers of the SSlEngine >> have to handle this exception and then continue using the engine for a >> proper close. That's just a side note. I (think) this behavior is not new. >> But it seems odd. >> At this point: >> Client engine.isOutboundDone() = false >> Client engine.isInboundDone() = true >> Still Client: >> 29. SSLEngineOutputRecord.java:465|WRITE: TLS12 alert, length = 2 >> 30. SSLCipher.java:1726|Plaintext before ENCRYPTION (0000: 02 0A >> ..) >> 31. 2018-08-04 05:16:34.300 KGT|SSLEngineOutputRecord.java:483|Raw write ( >> 0000: 15 03 03 00 1A 00 00 00 00 00 00 00 01 E2 36 F9 ..............6. >> 0010: 09 D2 20 65 B5 C9 04 CB D3 47 8B E2 CA 0B B2 .. e.....G..... >> ) >> - I believe this is unexpected_message. And I believe it is encrypted at >> this point as the client has sent the client finished message. This alert >> feels incorrect to me. The server sent "user_canceled". I feel like once >> that is sent, we should be able to receive "close_notify" without the client >> engine identifying this as an unexpected message. >> At this point: >> Client engine.isOutboundDone() = true >> Client engine.isInboundDone() = true >> That seems correct to me. We need to flush the last alert for the client and >> then we are done with the engine (although I believe the alert should be >> close_notify and not unexpected_message). >> At this point the server starts receiving all the handshake messages from >> the client. >> Server: >> 32. SSLEngineInputRecord.java:214|READ: TLSv1.2 handshake, length = 262 >> 33. RSAClientKeyExchange.java:279|Consuming RSA ClientKeyExchange handshake >> message >> 34. SSLEngineInputRecord.java:214|READ: TLSv1.2 change_cipher_spec, length = >> 1 >> 35. ChangeCipherSpec.java:143|Consuming ChangeCipherSpec message >> 36. SSLEngineInputRecord.java:214|READ: TLSv1.2 handshake, length = 40 >> 37. Finished.java:581|Consuming client Finished handshake message. >> At this point: >> Server engine.isOutboundDone() = false >> Server engine.isInboundDone() = false >> That seems completely wrong to me. We manually told the server outbound was >> done. But receiving the final handshake messages has put it back to outbound >> not being done. Additionally, at this point the server's handshake status is >> NEED_WRAP and NEED_TASK. It looks like it is trying to continue handshaking. >> Still Server: >> 38. ChangeCipherSpec.java:109|Produced ChangeCipherSpec message >> 39. Finished.java:450|Produced server Finished handshake message >> 40. SSLEngineInputRecord.java:214|READ: TLSv1.2 alert, length = 26 >> 41. Alert.java:232|Received alert message >> 42. TransportContext.java:312|Fatal (UNEXPECTED_MESSAGE): Received fatal >> alert: unexpected_message ( >> "throwable" : { >> javax.net.ssl.SSLProtocolException: Received fatal alert: >> unexpected_message >> at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:126) >> at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) >> at >> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:307) >> at >> java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:281) >> At this point the server engine has received the alert. That throws an >> exception. And the engine still seems to be in an incorrect "done" state. >> At this point: >> Server engine.isOutboundDone() = false >> Server engine.isInboundDone() = true >> Let me know if you have any questions. The particular point where we send >> close_notify from the server to the client is just incidental from how our >> "close during handshake" test works. Sending it at other points in the >> handshake process may produce different outcomes. >> Thanks, >> Tim >>> On Jul 30, 2018, at 12:13 PM, Xuelei Fan <xuelei....@oracle.com> wrote: >>> >>> Hi Tim, >>> >>> Would you mind look at the code I posted in the following thread: >>> http://mail.openjdk.java.net/pipermail/security-dev/2018-July/017708.html >>> >>> In the update, we are trying make the synchronization more simple and >>> robust. I appreciate if you could comment by the end of this week. >>> >>> Note that with this update, a complete TLS connection should close both >>> inbound and outbound explicitly. However, existing applications may not >>> did this way because TLS 1.2 and prior version can work around it. But for >>> TLS 1.3, it is possible to hang the application if the connection is not >>> closed. If the source code update is not available, please consider to use >>> the "jdk.tls.acknowledgeCloseNotify" System Property as a workaround. >>> >>> Thanks, >>> Xuelei >>> >>> On 7/18/2018 11:51 AM, Tim Brooks wrote: >>>> Yes. I can test once there is a patch. My inquiry was motivated by some >>>> work on Elasticsearch fyi. I can test a patch against that work. >>>> https://github.com/elastic/elasticsearch/issues/32144 >>>> - Tim >>>>> On Jul 17, 2018, at 8:40 PM, Xuelei Fan <xuelei....@oracle.com >>>>> <mailto:xuelei....@oracle.com>> wrote: >>>>> >>>>> Hi, >>>>> >>>>> We are working on the JDK 11 close issue. >>>>> https://bugs.openjdk.java.net/browse/JDK-8207009 >>>>> >>>>> I appreciate if you can help test if we have a patch. >>>>> >>>>> Thanks, >>>>> Xuelei >>>>> >>>>> On 7/17/2018 4:26 PM, Tim Brooks wrote: >>>>>> My understanding is that when you are interested in closing the >>>>>> underlying socket when using the SSLEngine, you must call >>>>>> closeOutbound() and WRAP and UNWRAP until both isInboundDone() and >>>>>> isOutboundDone() return true. >>>>>> One edge case of this is if you are interested in closing the socket >>>>>> prior to the completion of a handshake. In JDK 10.0.1 (and I believe >>>>>> prior JDKs) this was the behavior for one way in which this arises: >>>>>> 1. Initiate handshake >>>>>> 2. UNWRAP data from client >>>>>> 3. WRAP data to send to client. Handshake status is "NEED_UNWRAP" >>>>>> 4. Call closeOutbound() (perhaps the server is shutting down and you >>>>>> want to close the connection). >>>>>> 5. Handshake status now returns "NEED_WRAP" >>>>>> JDK10: >>>>>> isInboundDone() - returns false >>>>>> isOutboundDone() - returns false >>>>>> A call to wrap() produces 7 bytes and status = CLOSED. Handshake status >>>>>> is now NEED_UNWRAP. >>>>>> isInboundDone() - returns false >>>>>> isOutboundDone() - returns true >>>>>> JDK11: >>>>>> isInboundDone() - returns true >>>>>> isOutboundDone() - returns false >>>>>> A call to wrap() throws the following exception: >>>>>> javax.net.ssl.SSLException: Cannot kickstart, the connection is broken >>>>>> or closed >>>>>> at >>>>>> java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:205) >>>>>> at >>>>>> java.base/sun.security.ssl.SSLEngineImpl.writeRecord(SSLEngineImpl.java:167) >>>>>> at java.base/sun.security.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:138) >>>>>> at java.base/sun.security.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:116) >>>>>> at java.base/javax.net.ssl.SSLEngine.wrap(SSLEngine.java:471) >>>>>> I’m not sure what the procedure for closing a connection prior to >>>>>> handshake completion is for TLS. But obviously this is a scenario that >>>>>> can arise. It seems wrong to me that the state transitions for the >>>>>> SSLEngine do not handle this. The fact that “isOutboundDone()” returns >>>>>> false, but I cannot WRAP seems to be an issue.