TLS with SNI in Java clients
There is an ongoing discussion on the security and trust working group for NHIN Direct regarding the IP-to-domain mapping problem that is created with traditional SSL. If an HISP (as defined by NHIN Direct) wants to host thousands of NHIN Direct "Health Domains" for providers, then it will an "artificially inflated cost" to have to purchase an IP for each of those domains.
Because Apache and OpenSSL have recently released TLS with support for the SNI extension, it is possible to use SNI as a solution to this problem on the server side. However, if we decide that we will allow server implementations of the NHINDirect transport layer to support TLS+SNI, then we must require that all clients support SNI too. OpenSSL based clients should do this by default and one could always us stunnel to implement an TLS+SNI aware client to proxy if your given programming language SSL implementation does not support SNI. It appears that native Java applications using OpenJDK do not yet support SNI, but I cannot get a straight answer out of that开发者_如何学编程 project. I know that there are OpenSSL Java libraries available but I have no idea if that would be considered viable.
Can you give me a "state of the art" summary of where TLS+SNI support is for Java clients? I need a Java implementers perspective on this.
JavaSE 7 has SNI Support in JSSE.
http://docs.oracle.com/javase/7/docs/technotes/guides/security/enhancements-7.html
Note, there seems to be a problem with it, as you can read here:
SSL handshake alert: unrecognized_name error since upgrade to Java 1.7.0
it is also possible to patch with some lines the orig Sun JDK (bootclasspath) to get Server SNI working.
Class: sun.security.ssl.ServerHandshaker
Add Field
/** Use for SNI */
private ServerNameExtension serverNameExtension = null;
Patch Method clientHello (add these lines)
/* Use for SNI */
this.serverNameExtension = (ServerNameExtension)mesg.extensions.get(ExtensionType.EXT_SERVER_NAME);
Patch Method setupPrivateKeyAndChain (change)
if (this.conn != null) { alias = km.chooseServerAlias(algorithm , null, this.conn);
} else { alias = km.chooseEngineServerAlias(algorithm, null, this.engine); }
to
final Principal[] principals = (this.serverNameExtension == null) ? null : this.serverNameExtension.getHostnamePrincipals();
if (this.conn != null) { alias = km.chooseServerAlias(algorithm , principals, this.conn);
} else { alias = km.chooseEngineServerAlias(algorithm, principals, this.engine); }
Add to Class sun.security.ssl.ServerNameExtension
static final class ServerNamePrincipal implements Principal {
private final String name;
ServerNamePrincipal(final String name) { this.name = name; }
@Override public String getName() { return this.name; }
@Override public String toString() { return this.name; }
}
public Principal[] getHostnamePrincipals() {
final List<Principal> principals = new LinkedList<>();
for(final ServerName name : this.names) {
if(name.type == NAME_HOST_NAME) { principals.add(new ServerNamePrincipal(name.hostname)); }
}
return principals.toArray(new Principal[principals.size()]);
}
I'm working on the same project as ftrotter.
Note the requirement of support for thousands of domains. I don't think that SANs are going to cut the mustard for two reasons. First, the size of the certificate is going to get enormous, which will likely cause performance problems at a minimum. Second, these domains are going to come and go frequently, particularly in the early days of NHIN Direct. The operational burden of having to update the certificate every time a domain comes or goes, is going to be unacceptable, IMHO.
At ftrotter's request, I did some googling around on the subject of java, TLS and SNI, and other ways to implement what amounts to a named-based virtual hosting situation, with one certificate per virtual host. Here's what I've come up with:
JSSE (Java Secure Socket Extension) supports TLS, and has "partial support" for TLS+SNI. I have no idea what partial support means in this context. The commentary I'm seeing indicates that the support that exists is not adequate for doing named-based virtual hosts, which is basically what we need.
I've found one article that claims the JDK7 version of JSSE will support TLS+SNI (dated 11/20/2008), and I've found one that claims it won't (dated 2/27/2009). Neither is particularly authoritative.
Some of the folks working on OpenJDK 7 discussed the issues around adding SNI support to JSSE back in Feb-Mar 2009, including posting a source patch. (thread starts here: http://www.mail-archive.com/security-dev@openjdk.java.net/msg00612.html). OpenJDK7 isn't going to be released any time before about September 2010. I have no idea when the Java 7 platform will be released.
There is nothing substantive on java.sun.com at all, so I really don't know what Sun's plans are at all.
There is apparently a different way to accomplish name-based virtual hosts which is apparently widely compatible, using a single certificate per hosting server which contains multiple common names and multiple subject alt names. See http://wiki.cacert.org/VhostTaskForce and Serve different certs for same Tomcat application via connectors?
This approach would create really large certificates (due to all those CNs and SANs) if you have lots of virtual hosts. One of the folks at NHIN Direct's recent face-to-face meeting was talking about wanting to support thousands of virtual hosts. My guess is that this will break a lot of implementations. In addition, having to update the certificate each time you add or remove a virtual host sounds like a ridiculous operational burden.
In summary, the current Java state of the art for name-based virtual hosting with separate certificates per virtual host appears to be "no can do". In addition, it's not clear when or if it will be added.
Does anyone agree or disagree? Does anyone know if the OpenJDK project has any intention of "backporting" SNI support for Java 6?
精彩评论