TLS ALPN Proposal

Bradford Wetmore bradford.wetmore at oracle.com
Sat May 30 00:26:41 UTC 2015


Simone,

I'm sorry for the delay in responding, I've been getting familiar with 
lambdas the last couple days, and how we might be able to apply it to 
the ALPNSelector code.  Interesting stuff.


To the question in this email.  I'll leave the previous discussion for 
context.  See my responses inline.

On 5/27/2015 4:47 AM, Simone Bordet wrote:
> Hi,
>
> On Tue, May 26, 2015 at 8:46 PM, Bradford Wetmore
> <bradford.wetmore at oracle.com> wrote:
>>> I am not sure I follow this. Can you please sketch the steps/algorithm
>>> that will be done in your proposed solution ?
>>
>> I'm assuming you are talking about 1a and not 1b.
>>
>> Sure, most of the heavy lifting takes place in ServerHandshaker.
>>
>> ServerHandshaker.java
>> =====================
>>
>> In the SunJSSE code:
>>
>>      // currently line 330.
>>      private void clientHello(ClientHello mesg) throws IOException {
>>
>>              // Was an ALPNExtension received?
>>              ALPNExtension alpnExt = (ALPNExtension)
>>                  mesg.extensions.get(ExtensionTyp.EXT_ALPN);
>> ...
>>              // Currently line 706 in JDK9.
>>              session = new SSLSessionImpl(protocolVersion,
>>                       CipherSuite.C_NULL,
>>                       getLocalSupportedSignAlgs(),
>>                       sslContext.getSecureRandom(),
>>                       getHostAddressSE(), getPortSE());
>> ...
>>              session.setALPNNames(alpnExt.getNames());
>> ...
>>              // choose cipher suite and corresponding private key
>>              // This function is at 987 currently.
>>              chooseCipherSuite(mesg);
>> ...
>>              // Only adds an ALPN extension if non-empty.
>>              m1.extensions.add(applicationProtocolSelector.select(...));
>> ...
>>
>> Above, chooseCipherSuite() iterates through the collection of suites. Inside
>> that, trySetCipherSuite() attempts to verify that the suite is acceptable to
>> use.  Inside that, setupPrivateKeyAndChain() does the actual KeyManager
>> calls.
>>
>>          if (conn != null) {
>>              alias = km.chooseServerAlias(algorithm, null, conn);
>>          } else {
>>              alias = km.chooseEngineServerAlias(algorithm, null, engine);
>>          }
>>
>> Now in the Application's Code:
>>
>> If you want the KeyManager to take this info into account, you need to
>> create your own customer KM.
>>
>> public String chooseEngineServerAlias(String keyType,
>>                                        Principal[] issuers,
>>                                        SSLEngine engine) {
>>
>>      ExtendedSSLSession session = engine.getHandshakeSession();
>>      String protocol = session.getProtocol();
>>      String ciphersuite = session.getCipherSuite();
>>      List<String> alpns =
>>          session.getRequestedApplicationProtocolNames();
>>
>>      // Some logic for which key to use...
>>      return choose(protocol, ciphersuite, alpns);
>> }
>>
>> And then your actual ALPN selector:
>>
>> public String select(...) throws SSLProtocolException {
>>      ExtendedSSLSession session = engine.getHandshakeSession()
>>      String ciphersuite = session.getCipherSuite();
>>      List<String> alpns =
>>          session.getRequestedApplicationProtocolNames();
>>
>>      // Some logic for which key to use...
>>      return choose(protocol, ciphersuite, alpns);
>> }
>>
>> Hopefully that is clear.
>
> Let me see if I understand correctly.
>
> In chooseEngineServerAlias() I will have a proposed cipher and the
> list of app protos from the client.
> The goal would be to choose the alias based on the app proto, as stated by 7301.
>
> However, the app proto is not yet chosen here. If I don't have other
> constraints to choose the app proto (e.g. SNI), the usual algorithm
> applies: pick the first server app proto that is in common with the
> client.
> Let's assume this is h2; but looking at the cipher, it's not good, so
> we have to pick another app proto, say http/1.1. Now the cipher is
> good and we return the alias.
> This is similar to what happens now with Jetty's ALPN: the cipher will
> be chosen, then the ALPN callback invoked and there, by looking at the
> cipher, we know we have to use http/1.1.
>
> Let's assume now I have the constraint to choose h2 (e.g. I have a SNI
> of http2.domain.com).
> In chooseEngineServerAlias() I will look at SNI, and therefore choose
> h2, then look at the proposed cipher, which is not good, so return
> null.
> Method chooseEngineServerAlias() will be called repeatedly for other
> ciphers, until a cipher good for h2 is found, otherwise it's an alert
> 120.

Correct.  To expand a bit...

You will have the SSLSocket/SSLEngine which will give you access to the 
Socket's attributes (e.g. local/remote IP address/ports), along with the 
handshakeSession which is being negotiated.  The handshakeSession will 
give access to the selected TLS protocol version number plus the 
received SNI and ALPN names.  With my change to the ciphersuites, it 
will now give you to the "proposed" ciphersuite, the one being probed 
for valid key material.  If your custom KeyManager don't like this 
combination of protocol/ciphersuite/sni/alpn/Socket attributes, the 
KeyManager can report back no valid key material is available, thus 
meeting that constraint.

> Are you proposing to call select(...) from chooseEngineServerAlias(),
> and give the ALPN callback a semantic that it may be called multiple
> times, each time with a different cipher, and give access to the ALPN
> callback implementation to SNI to choose the right protocol based on
> that ?

That's another approach, add an SSLSocket/SSLEngine API to set the 
selected value from the KM callback.  But that won't work for KM-less 
ciphersuites (anonymous).

I was planning on calling select() after the KM has returned to make the 
final selection once the ciphersuite has been officially selected, 
rather than requiring the KM to make the call.

Brad



More information about the security-dev mailing list