I just would like to remind that session resumption is a very
important use case to support for ALPN.

Understood. The ALPN value is tied to a handshake, either already completed and active (getApplicationProtocol()) or still in progress (getHandshakeApplicationProtocol()). Each handshake results in a complete ALPN negotiation. So a session resumption will always do a ALPN negotiation from scratch.

I have not seen anything related to this review for session resumption.
Has it been tested ?

The current unit tests don't have resumption, but there should be a RFE (if not already) for resumption testing along with some other cases. Vinnie is working on these.

It is still not clear to me what a client and a server have to do to
support ALPN.

If you want to use the default behavior (server-prioritized list looking for a common element), just call SSLParameters.setApplicationProtocols() on both sides.

If you want to do very specific protocol/ciphersuite/alpn configuration before handshaking, then do the configuration before starting the handshake (more details and similar to what you suggested below). This is what was decided back in October when we pulled out the Matcher mechanism in v6/v7. [1]

 From my understanding a server has to:

* read encrypted bytes into a ByteBuffer

BTW, during the initial handshake on a connection, these are not encrypted.

* parse the TLS ClientHello frame on its own
* extract from the ClientHello the TLS Protocol version, the ALPN
extension, and the ciphers
* run some logic to determine the AP
** if no AP can be chosen, generate a TLS Alert frame on its own to
close the connection and bail out

I would create a regular SSLContext/SSLEngine/configureALPN and start the handshake as usual. The JSSE implementation will create the TLS ALPN Alert when it realizes it doesn't have any ALPN values in common. The server then should obtain those bytes from the wrap() and send to the peer, as usual.

* otherwise, an AP/cipher combo has been chosen

Filling in a few more details:

SSLContext.getInstance("protocol"); // returns a context with
                                    // "protocol" and possibly
                                    // other protocols enabled.
SSLEngine ssle = SSLContext.createSSLEngine(...);

Read ClientHello from Socket/SocketChannel/AsynchronousSocketChannel/etc.

Parse ClientHello for requested protocol/alpn/ciphersuites

choose protocol/alpn/ciphersuite value(s)

SSLParameters sslParameters = ssle.getSSLParameters();
sslParameters.setProtocols(protocols);        // possibly just one
    // You could use some non-matching value like
    // "no_application_protocol" if you want to generate the
    // no_application_protocol alert.
sslParameters.setApplicationProtocols(APs);   // possibly just one
sslParameters.setCipherSuites(ciphers)        // possibly just one

sslEngine.setSSLParameters(sslParameters)

reset the ByteBuffer position to the beginning

sslEngine.unwrap()

JDK implementation will re-parse the ClientHello, and use the
sslParameters data during handshake and when generating the
ServerHello.  Any error alerts will be output by the SSLEngine as usual.

sslEngine.wrap()/unwrap() as usual.

A client has to:

* read encrypted bytes into a ByteBuffer
* parse a ServerHello frame on its own
* extract the ALPN extension and the cipher

You could parse this if you want, but wasn't expecting client apps would. See below.

* run some logic to verify that the AP and the cipher can be used together
** if AP and cipher don't match, generate a TLS Alert frame on its own
to close the connection and bail out.

There is nothing in the ALPN RFC about this situation. In the HTTP spec, if such a situation is negotiated, the application layer waits for the handshake completion, examines the state, sends a HTTP2 connection error of type INADEQUATE_SECURITY, then closes.

BTW, we would lose the ability to parse plaintext ServerHellos in TLSv1.3, as ServerHello is now encrypted.

* otherwise the AP/cipher combo is ok
* reset the ByteBuffer position to the beginning
* pass the ByteBuffer to sslEngine.unwrap()
* JDK implementation will re-parse the ServerHello and complete the
TLS handshake

Is that right ?

If you really want to parse it, yes.

In that scenario, what is the use of
SSLEngine.getHandshakeApplicationProtocol() ?

When either side needs to determine the selected ALPN value before the completion of the handshake, say during X509KeyManager selection or X509TrustManager verification. On the server side, let's say we have two possible keys/certs, and we receive a ClientHello.

ClientHello  ->
                  JSSE chooses protocol/ALPN
                  JSSE iterates on ciphersuite
                  iter calls X509KeyManager to find a key
                    X509KM calls setHandshakeApplicationProtocol()
                    to see which protocol/ALPN value was negotiated,
                    uses that for selecting which key to use.
             <- ServerHello

Also, I don't understand how the above could work for SSLSocket ?
Can someone write down the steps a client and a server should do to
actually use ALPN with SSLSocket ?

Server
======

Filing in a few more extra details so it's hopefully clear.

Open a plaintext ServerSocket
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
byte[] ba = is.readFully();

// extract from the ClientHello the TLS Protocol version, the ALPN
// extension, and the ciphers
parseTLSClientHello(ba);

determineProtocolALPNCiphersuites();

SSLContext ctx = \
    SSLContext.getInstance("protocol") // returns a context with
                                   // "protocol" and possibly
                                   // other protocols enabled.

SSLParameters sslParameters = ctx.getDefaultSSLParameters();

SSLParameters sslParameters =
    sslParameters.setProtocols(protocol);
sslParameters.setApplicationProtocols(AP) // just one protocol
sslParameters.setCipherSuites(cipher) // just one cipher

// Overlay the partially consumed InputStream
ByteArrayInputStream bais = new ByteArrayInputStream(ba);

SSLSocket sslSocket = \
    ctx.createSocketFactory(...).createSocket(socket, bais, ...);
sslSocket.setSSLParameters(sslParameters)
sslSocket.startHandshake();

Brad

[1] http://mail.openjdk.java.net/pipermail/security-dev/2015-October/012912.html

Reply via email to