TLS ALPN Proposal
Bradford Wetmore
bradford.wetmore at oracle.com
Sat May 23 01:13:37 UTC 2015
Thanks for the thorough reviews and comments, I really appreciate it and
always learn something. FunctionalInterface (@since 1.8) is something I
haven't really explored yet, so off to the books.
I'm glad this ALPN approach seems worth pursing. I have several
different comments I'll combine into this single message.
On 5/22/2015 9:12 AM, Simone Bordet wrote:
>> ExtendedSSLSession:
>> -------------------
>> gets the negotiated protocol String
>
> If I understand correctly, SSLParameters will have methods that can
> only be used by one side of the TLS protocol (e.g. client or server,
> but not both).
> It's already like this for other things (e.g. SNI) so it will work,
> but I had hoped for some kind of reworking in this area too, but I'm
> digressing.
I don't have a better idea/suggestion for this, protocol advertisement
and selection are two very different things. At this point, I am
thinking it should remain in SSLParameters.
I considered putting the selector into the SSLContext initialization:
sslContext.init(KeyManager, TrustManager, SecureRandom, Selector);
but that means 1 selector for all SSLSockets/SSLEngines that get
generated from that SSLContext. That is constricting if the selector
wanted to keep any connection-specific values. It could go into
SSLSocket/SSLEngine, but starting in JDK 6, we've been lumping all set*
configuration parameters into SSLParameters, so that once configured,
devs can reuse one SSLParameters object without having to reconstruct it
(source code or at runtime) from scratch.
BTW, there was a previous question about when to use SSLSocket.set*()
and sslSocket.setSSLParameters(params). I gave a partial answer in:
http://mail.openjdk.java.net/pipermail/security-dev/2014-November/011430.html
I recently filed:
https://bugs.openjdk.java.net/browse/JDK-8080799
Provide guidance on repeated configuration parameter APIs
to address this. I don't think we need to deprecate the set() methods,
but having a note here will alleviate confusion.
>> /*
>> * A callback class on the server side that is responsible for
>> * choosing which Application Protocol should be used in a SSL/TLS
>> * connection.
>> */
>> public abstract class ApplicationProtocolSelector {
>>
>> /*
>> * SSLSocket callback to choose which Application Protocol value
>> * to return to a SSL/TLS client
>> *
>> * @param sslSocket the SSLSocket for this connection
>> * @param protocols the list of protocols advertised by the client
>> * @param protocolVersion the negotiated protocol version
Egads, this is confusing. protocols is the ALPN protocols,
protocolVersion is the TLS version number. I'll fix this.
>> * @param ciphersuite the negotiated ciphersuite
>> * @return a non-empty protocol String to send to the client,
>> * or null if no protocol value (i.e. extension) should be sent.
>> * @throws SSLProtocolException if the connection should be aborted
>> * because the the server supports none of the protocols that
>> * the client advertised.
Whoops, I put exactly the Exception I didn't want to use! I was
originally thinking SSLHandshakeException. See below for more comments.
>> */
>> public String select(SSLSocket sslSocket, String[] protocols,
>> String protocolVersion, String ciphersuite)
>> throws SSLProtocolException;
>
> We are currently getting the protocolVersion and the cipherSuite via:
>
> [sslSocket|sslEngine].getHandshakeSession().get[Protocol|CipherSuite]()
>
> If this is correct, perhaps there is no need to pass those 2
> parameters to select() ?
Good point. Yes, those are no longer needed.
There was a suggestion to have access to the clientHello information in
order to guide ciphersuite/protocol selection, however, there isn't a
way to control that part of the internals at this time, so I didn't
include it.
As an aside, if we do develop the Handshake API (other mail) and allow
for handshake message modification (not currently proposed, but hinted
at), the protocols/ciphersuites values could be adjusted before they are
handled. e.g. INBOUND callbacks would be triggered after the message
has been parsed and added to the handshake hash calculation, but before
clientHello() is actually called.
> I would suggest that if SSLParameters.setApplicationProtocols() takes
> a List<String>, then also select() should take a List<String>, rather
> than a String[].
> Since it's the server picking the protocols, would be handy to have
> the client protocols in a List<String> in order to call contains(),
> etc. on it.
Good points.
> I would suggest to throw SSLException rather than the too specific
> SSLProtocolException (which may also be misleading, since its javadoc
> hints at a "flaw in one of the protocol implementations", while in
> this case it is just a failure to negotiate an *application* protocol
> - perfectly fine from the point of view of the TLS protocol).
See above. I'll change it to SSLException for now, but I think it
probably should be SSLHandshakeException.
> Would be great if we could make this class a FunctionalInterface, but
> I guess it's not easy due to lack of commonality between SSLSocket and
> SSLEngine.
> Unless there is a way to abstract something out of those 2 :)
I'll look into Sean's suggest of SSLBase.
>> public class ExtendedSSLSession implements SSLSession {
>>
>> ...deleted...
>>
>> /**
>> * Gets the application protocol negotiated for this connection.
>> *
>> * @returns the application protocol name or null if none was
>> * negotiated.
>> */
>> public String getApplicationProtocol() { };
>> }
>
> How would this case be covered:
>
> * client sends protocol list "foo","bar"
> * server sends back "bax".
> * client has to decide what to do.
Are you positive this was a valid mode of operation? From RFC 7301:
With ALPN, the client sends the list of supported application
protocols as part of the TLS ClientHello message. The server chooses
a protocol and sends the selected protocol as part of the TLS
ServerHello message.
This says to me that it needs to be one of the offered protocols.
Granted, it's not explictly stated "it must be one of the offered", but
allowing for that doesn't make sense to me. Should the server really be
allowed to select a protocol that the client didn't offer, and then
realistically expect the client to support it? If the server supported
it, the client should have offered in the first place.
I was originally thinking that we should throw an Exception if the
negotiated protocol is not one of those sent, but maybe that's better
left to the application when it looks at the ALPN value after
handshaking is completed.
> The only chance to call ExtendedSSLSession.getApplicationProtocol()
> would be *after* the handshake is completed, right ?
I think this point is moot, but to answer your question.
For SSLEngine, it could be done at any time.
For SSLSocket, if the connection needed to use the KeyManager(client
auth)/TrustManager(server cert verification), then it could call during
that point.
> Do you plan to hardcode the abort of the handshake with an alert_120,
> or give the application a chance (just asking) ?
Originally, yes. To support what you suggest, we would need to add a
level/description to the SSLException class. I'd been debating that
anyway, but this RFC was pretty clear that only 120 is needed.
>> There was also some internal discussion about whether the values should be
>> Strings or byte arrays. The ALPN RFC only discusses bytes, and a
>> String.toBytes("US-ASCII") would limit the API to ASCII strings.
>>
>> Lastly, we could also add some convenience values for well-known values.
>> e.g.:
>>
>> public static final AP_HTTP_1.1 = "http/1.1";
>>
>> or in byte form:
>>
>> public static final AP_H2 = "h2".getBytes(""US-ASCII");
>>
>> I refrained from including SPDY/*, since they are on their way out now, and
>> NAT/STUN since there hasn't been any call for it so far.
>
> I agree that only http/1.1 and h2 deserve a constant.
Ok.
Sean wrote:
> There's actually a bunch of methods in common, so one possibility is
> to create a new interface containing the common methods (say SSLBase
> for now for lack of a better name). Then you could change SSLEngine
> and SSLSocket to implement SSLBase (which should be a compatible
> change), and then have a single method on ApplicationProtocolSelector.
Worth looking into. Thanks for the suggestion.
Simon wrote:
> For HTTP/2, however, the choice of the protocol will depend on the TLS
> version and the cipher, so we will need a way to obtain that
> information.
Yes, that's why I originally included the negotiated
protocol/ciphersuite (er...soon to be sslBase.getHandshakeSession()).
HTTP/2 must be TLSv1.2 and the ciphersuite must not be on the blacklist.
> Typically, in the code where you pass the ApplicationProtocolSelector
> you have the SSLEngine available, e.g.:
>
> sslEngine.getSSLParameters().setApplicationProtocolSelector(new
> ApplicationProtocolSelector()
> {
> public String select(List<String> clientProtocols)
> {
> // Here sslEngine will be in lexical scope
> }
> });
>
> In this way, ApplicationProtocolSelector won't depend on SSLEngine or
> SSLSocket, and can be reduced to a FunctionalInterface.
But during the callback, does the application have access to the SSLEngine?
Unless I've haven't grasped how FunctionalInterfaces would have access
to SSLEngine/SSLSocket objects here (admittedly I'm flying a little
blind here), the application might want to make the protocol decision
based on other factors such as network origin. Our original
X509TrustManager didn't have access to the Socket/Engine, and it was
commonly requested.
> RFC 7301 hints that protocol string could be UTF-8 encoded:
> http://tools.ietf.org/html/rfc7301#section-6
Thanks for the pointer. We could add to the javadoc that it will be
handled as UTF-8?
Weijun wrote:
> But in the RFC the name is in uppercase and chars in string are all
> lowercases.
> ...deleted...
> - Compare with equalsIgnoreCase()
Not following here, the spec is specific about the over-the-wire byte
values, and http/1.1 != Http/1.1.
The conversion inside SSLParameters could be done as:
String s = new String(ba, "UTF-8");
byte[] ba = s.getBytes("UTF-8");
I would love any thoughts on the Handshake API approach, but understand
it's a lot to parse, and ALPN should take priority for the immediate future.
I should have an update out early next week, and hopefully can move this
JEP to "Proposed to Target" if there are no other major comments.
Thanks again,
Brad
More information about the security-dev
mailing list