[security-dev 00235]: Re: X509KeyManager alias choice based on temporary socket

Andrew Fan Xuelei.Fan at Sun.COM
Wed Jul 9 17:37:26 PDT 2008


Bruno Harbulot wrote:
> Hi Andrew,
>
> OK, but the problem is precisely that this 
> "socket.getLocalAddress().isAnyLocalAddress()" is always true, 
> regardless of what the actual socket has been configured with. 
> "socket.getLocalAddress()" always returns 0.0.0.0, because this 
> information isn't passed from SSLServerSocketImpl to the temporary 
> SSLSocketImpl, which is then passed to chooseServerAlias(). This 
> workaround cannot work.
>
The X509KeyManager.chooseServerAlias() may be called two ways, one is 
just as your description, a temporary socket used via 
SSLServerSocketImpl.checkEnabledSuites(). Once the check passed, the 
server socket will not try to check it against the key manager anymore.

The other way, while handshaking, the X509KeyManager.chooseServerAlias() 
will be called with the actual socket. The stack looks like,
X509KeyManager.chooseServerAlias()
ServerHandshaker.setupPrivateKeyAndChain()
trySetCipherSuite
chooseCipherSuite
ServerHandshaker.clientHello()
Handshaker.processMessage()

Please have a try with a workaround, any feedback are welcome.

Regards,
Andrew
> Best wishes,
>
> Bruno.
>
>
> Andrew Fan wrote:
>> Hi Bruno,
>>
>> Suppose the expected customized X509KeyManager looks like:
>> MyX509KeyManager extends X509KeyManager {
>>    ... chooseServerAlias(..., Socket socket) {
>>         if (socket.getLocalAddress() equals to a localAddress, 
>> 10.0.0.1) {
>>              choose the certificate for 10.0.0.1.
>>         }
>>
>>         if (socket.getLocalAddress() equals to the 2nd localAddress, 
>> 10.0.0.2) {
>>              choose the certificate for 10.0.0.2.
>>         }
>>        ...
>>   }
>> }
>>
>> My workaround looks like:
>> MyX509KeyManager extends X509KeyManager {
>>    ... chooseServerAlias(..., Socket socket) {
>>
>>         if (socket.getLocalAddress().isAnyLocalAddress()) {  // used 
>> to match 0.0.0.0 or ::0
>>              choose the certificate for 10.0.0.1.
>>              // or
>>              // for ip in 10.0.0.1, 10.0.0.2, 10.0.0.3 {
>>              //     if ( choose certificate from ip sucess) {
>>              //         break;
>>              //     }
>>              // }
>>         }
>>
>>         if (socket.getLocalAddress() equals to a localAddress, 
>> 10.0.0.1) {
>>              choose the certificate for 10.0.0.1.
>>         }
>>
>>         if (socket.getLocalAddress() equals to the 2nd localAddress, 
>> 10.0.0.2) {
>>              choose the certificate for 10.0.0.2.
>>         }
>>        ...
>>   }
>> }
>>
>> Regards,
>> Andrew
>>
>>
>> Bruno Harbulot wrote:
>>> Hello Andrew,
>>>
>>> I'm not sure I understand the workaround. I don't think there is any.
>>>
>>> I want to be able to choose the alias in the X509KeyManager based on 
>>> the IP address on which the server socket is bound.
>>>
>>> There are several sockets in the same application bound to different 
>>> addresses (for example 10.0.0.1, 10.0.0.2, 127.0.0.1, each having a 
>>> different host name). All these addresses are local, and binding a 
>>> socket to a particular IP address rather than the wildcard address 
>>> (0.0.0.0) works fine.
>>>
>>> What I want chooseServerAlias() to know is which of these addresses 
>>> is used, in order to pick the appropriate certificate (since the 
>>> certificate name has to match the host name and these IP addresses 
>>> have distinct host names).
>>>
>>> Choosing a certificate based on which IP address the socket will 
>>> accept connections on is currently impossible because the temporary 
>>> socket isn't initialised with any local address (or port) 
>>> information, which is already available at that stage (since it 
>>> initialised either in the constructor or within bind(), before an 
>>> accept()).
>>>
>>> If SSLSocketImpl wasn't final, something along these lines would 
>>> probably work:
>>>
>>>    SSLSocketImpl tmp = new SSLSocketImpl(sslContext, useServerMode,
>>>       enabledCipherSuites, doClientAuth,
>>>       enableSessionCreation, enabledProtocols) {
>>>          public SocketAddress getLocalSocketAddress() {
>>>             return SSLServerSocketImpl.this.getLocalSocketAddress();
>>>          }
>>>    }
>>>
>>>
>>> Unfortunately, there doesn't seem to be a way to pass on this local 
>>> address/port information using this way of constructing the 
>>> temporary SSLSocketImpl, and there doesn't this to be a way to set 
>>> this information afterwards.
>>>
>>> A fix would require a way to set up the local address/port in the 
>>> tmp SSLSocketImpl, either by having a constructor that supports this 
>>> feature or a setter afterwards (probably less good, there's a good 
>>> reason why there no setLocalSocketAddress() method).
>>>
>>>
>>> Best wishes,
>>>
>>> Bruno.
>>>
>>>
>>>
>>> Andrew Fan wrote:
>>>> Bruno Harbulot,
>>>>
>>>> The issue has been reported as a bug. If you cannot wait for a fix, 
>>>> please try a workaround as described bellow.
>>>>
>>>> The SSLServerSocketImpl.checkEnabledSuites() is called before a 
>>>> socket connection accept(i.e, no socket created at the call time), 
>>>> so a temporary socket generated in order to build a context to 
>>>> check enabled cipher suites. However, for your requirements, the 
>>>> solution runs into the corner, and broke to find a proper cipher 
>>>> suite.
>>>>
>>>> The SunJSSE current implementation will generated the socket on in 
>>>> a wildcard address. As a workaround, the customized 
>>>> chooseServerAlias() could regarded the wildcard address as one of 
>>>> your actual address, (by checking 
>>>> socket.getLocalAddress().isAnyLocalAddress).
>>>>
>>>> Once the SSLServerSocketImpl.checkEnabledSuites() passed, the 
>>>> following accepted socket will use the actual socket, the behavior 
>>>> is just as your expect.
>>>>
>>>> Regards,
>>>> Andrew
>>>>
>>>> Bruno Harbulot wrote:
>>>>> Hello,
>>>>>
>>>>> I'm trying to use an X509KeyManager to choose which certificate a 
>>>>> server presents depending on which IP address the socket is 
>>>>> listening on.
>>>>>
>>>>> Let's suppose I have two certificates (+private keys) for 
>>>>> host1.example.org (10.0.0.1) and host2.example.com (10.0.0.2), and 
>>>>> that the clients are going to check the name of the hostname 
>>>>> against the name in the certificate. This should work fine since 
>>>>> there are two distinct IP addresses, one for each certificate.
>>>>>
>>>>> The Java server I'm running is configured with a single 
>>>>> SSLContext, which is set up with a KeyStore that contains two 
>>>>> pairs of private keys and certificates. This SSLContext is also 
>>>>> set up to use a custom X509KeyManager, which I was planning to use 
>>>>> to choose which of the two aliases (and therefore certificates) 
>>>>> should be used depending on the socket.
>>>>> This server starts up two SSLServerSockets, one with local address 
>>>>> 10.0.0.1 and the other one with local address 10.0.0.2 (same port, 
>>>>> but this shouldn't really matter).
>>>>>
>>>>> I initially thought that "chooseServerAlias(String keyType, 
>>>>> Principal[] issuers, Socket socket)" would give me the server 
>>>>> socket used and thus I would be able to pick the alias based on 
>>>>> "socket.getLocalAddress()".
>>>>>
>>>>> Unfortunately, it turns out that the socket passed to 
>>>>> chooseServerAlias is not the socket that is actually used. Its 
>>>>> address is always 0.0.0.0, regardless of the IP address the actual 
>>>>> listening socket has been bound to.
>>>>>
>>>>> I've traced the call to chooseServerAlias to:
>>>>> com.sun.net.ssl.internal.ssl.ServerHandshaker.setupPrivateKeyAndChain(ServerHandshaker.java:843) 
>>>>>
>>>>>     at 
>>>>> com.sun.net.ssl.internal.ssl.ServerHandshaker.trySetCipherSuite(ServerHandshaker.java:686) 
>>>>>
>>>>>     at 
>>>>> com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.checkEnabledSuites(SSLServerSocketImpl.java:292) 
>>>>>
>>>>>     at 
>>>>> com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.accept(SSLServerSocketImpl.java:253) 
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Looking at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl (in 
>>>>> openjdk-6-src-b10_30_may_2008.tar.gz and 
>>>>> openjdk-7-ea-src-b30-03_jul_2008.zip), it turns out that the 
>>>>> choice of the certificate is based on a temporary socket, which is 
>>>>> initialised with settings based on the actual socket (cipher 
>>>>> suites, etc.), but not the local address or port. (Code fragment 
>>>>> at the end of this e-mail.)
>>>>>
>>>>>
>>>>> It is done by design? Any idea how it would be possible to choose 
>>>>> a certificate based on which IP address is used otherwise?
>>>>>
>>>>>
>>>>> Best wishes,
>>>>>
>>>>> Bruno.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> ====== This is a short extract of 
>>>>> com.sun.net.ssl.internal.ssl.SSLServerSocketImpl (line count will 
>>>>> differ depending on the version, but it's towards the end of the 
>>>>> file) =====
>>>>>
>>>>>     /*
>>>>>      * This is a sometimes helpful diagnostic check that is performed
>>>>>      * once for each ServerSocket to verify that the initial set of
>>>>>      * enabled suites are capable of supporting a successful 
>>>>> handshake.
>>>>>      */
>>>>>     private void checkEnabledSuites() throws IOException {
>>>>>         //
>>>>>         // We want to report an error if no cipher suites were 
>>>>> actually
>>>>>         // enabled, since this is an error users are known to 
>>>>> make.  Then
>>>>>         // they get vastly confused by having clients report an 
>>>>> error!
>>>>>         //
>>>>>         synchronized (this) {
>>>>>             if (checkedEnabled) {
>>>>>                 return;
>>>>>             }
>>>>>             if (useServerMode == false) {
>>>>>                 return;
>>>>>             }
>>>>>
>>>>>             SSLSocketImpl tmp = new SSLSocketImpl(sslContext, 
>>>>> useServerMode,
>>>>>                          enabledCipherSuites, doClientAuth,
>>>>>                          enableSessionCreation, enabledProtocols);
>>>>>
>>>>>             ServerHandshaker handshaker = tmp.getServerHandshaker();
>>>>>
>>>>>             for (Iterator t = enabledCipherSuites.iterator(); 
>>>>> t.hasNext(); ) {
>>>>>                 CipherSuite suite = (CipherSuite)t.next();
>>>>>                 if (handshaker.trySetCipherSuite(suite)) {
>>>>>                     checkedEnabled = true;
>>>>>                     return;
>>>>>                 }
>>>>>             }
>>>>>
>>>>
>>




More information about the security-dev mailing list