Request for review: 8144093: JEP 244/8051498 - TLS Application-Layer Protocol Negotiation Extension

Bradford Wetmore bradford.wetmore at oracle.com
Wed Dec 2 00:04:41 UTC 2015


> 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


More information about the security-dev mailing list