[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