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

Andrew Fan Xuelei.Fan at Sun.COM
Wed Jul 9 16:43:17 UTC 2008


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