Add support for SNIHostName when using Java8+

Motivation:

Java8 added support for using SNIHostName with SSLParameters. We currently ignore it in OpenSslEngine.

Modifications:

Use reflection to support SNIHostName.

Result:

People using Java8 can use SNIHostName even when OpenSslEngine is used.
This commit is contained in:
Norman Maurer 2016-06-16 12:04:39 +02:00
parent ee0897a1d9
commit e14a385a88

View File

@ -27,11 +27,14 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.Buffer;
import org.apache.tomcat.jni.SSL;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -78,6 +81,11 @@ public final class OpenSslEngine extends SSLEngine {
private static final SSLException ENGINE_CLOSED = new SSLException("engine closed");
private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException("renegotiation unsupported");
private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException("encrypted packet oversized");
private static final Class<?> SNI_HOSTNAME_CLASS;
private static final Method GET_SERVER_NAMES_METHOD;
private static final Method SET_SERVER_NAMES_METHOD;
private static final Method GET_ASCII_NAME_METHOD;
static {
ENGINE_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
RENEGOTIATION_UNSUPPORTED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
@ -89,6 +97,37 @@ public final class OpenSslEngine extends SSLEngine {
destroyedUpdater = AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
}
DESTROYED_UPDATER = destroyedUpdater;
Class<?> sniHostNameClass = null;
Method getAsciiNameMethod = null;
Method getServerNamesMethod = null;
Method setServerNamesMethod = null;
if (PlatformDependent.javaVersion() >= 8) {
try {
sniHostNameClass = Class.forName("javax.net.ssl.SNIHostName", false,
PlatformDependent.getClassLoader(OpenSslEngine.class));
Object sniHostName = sniHostNameClass.getConstructor(String.class).newInstance("netty.io");
getAsciiNameMethod = sniHostNameClass.getDeclaredMethod("getAsciiName");
@SuppressWarnings("unused")
String name = (String) getAsciiNameMethod.invoke(sniHostName);
getServerNamesMethod = SSLParameters.class.getDeclaredMethod("getServerNames");
setServerNamesMethod = SSLParameters.class.getDeclaredMethod("setServerNames", List.class);
SSLParameters parameters = new SSLParameters();
@SuppressWarnings({ "rawtypes", "unused" })
List serverNames = (List) getServerNamesMethod.invoke(parameters);
setServerNamesMethod.invoke(parameters, Collections.emptyList());
} catch (Throwable ingore) {
sniHostNameClass = null;
getAsciiNameMethod = null;
getServerNamesMethod = null;
setServerNamesMethod = null;
}
}
SNI_HOSTNAME_CLASS = sniHostNameClass;
GET_ASCII_NAME_METHOD = getAsciiNameMethod;
GET_SERVER_NAMES_METHOD = getServerNamesMethod;
SET_SERVER_NAMES_METHOD = setServerNamesMethod;
}
private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
@ -160,9 +199,10 @@ public final class OpenSslEngine extends SSLEngine {
private volatile ClientAuth clientAuth = ClientAuth.NONE;
private volatile String endPointIdentificationAlgorithm;
private String endPointIdentificationAlgorithm;
// Store as object as AlgorithmConstraints only exists since java 7.
private volatile Object algorithmConstraints;
private Object algorithmConstraints;
private List<?> sniHostNames;
// SSL Engine status variables
private boolean isInboundDone;
@ -1393,23 +1433,55 @@ public final class OpenSslEngine extends SSLEngine {
}
@Override
public SSLParameters getSSLParameters() {
public synchronized SSLParameters getSSLParameters() {
SSLParameters sslParameters = super.getSSLParameters();
if (PlatformDependent.javaVersion() >= 7) {
int version = PlatformDependent.javaVersion();
if (version >= 7) {
sslParameters.setEndpointIdentificationAlgorithm(endPointIdentificationAlgorithm);
SslParametersUtils.setAlgorithmConstraints(sslParameters, algorithmConstraints);
if (version >= 8 && SET_SERVER_NAMES_METHOD != null && sniHostNames != null) {
try {
SET_SERVER_NAMES_METHOD.invoke(sslParameters, sniHostNames);
} catch (IllegalAccessException e) {
throw new Error(e);
} catch (InvocationTargetException e) {
throw new Error(e);
}
}
}
return sslParameters;
}
@Override
public void setSSLParameters(SSLParameters sslParameters) {
public synchronized void setSSLParameters(SSLParameters sslParameters) {
super.setSSLParameters(sslParameters);
if (PlatformDependent.javaVersion() >= 7) {
int version = PlatformDependent.javaVersion();
if (version >= 7) {
endPointIdentificationAlgorithm = sslParameters.getEndpointIdentificationAlgorithm();
algorithmConstraints = sslParameters.getAlgorithmConstraints();
if (version >= 8 && SNI_HOSTNAME_CLASS != null && clientMode && !isDestroyed()) {
assert GET_SERVER_NAMES_METHOD != null;
assert GET_ASCII_NAME_METHOD != null;
try {
List<?> servernames = (List<?>) GET_SERVER_NAMES_METHOD.invoke(sslParameters);
for (Object serverName : servernames) {
if (SNI_HOSTNAME_CLASS.isInstance(serverName)) {
SSL.setTlsExtHostName(ssl, (String) GET_ASCII_NAME_METHOD.invoke(serverName));
} else {
throw new IllegalArgumentException("Only " + SNI_HOSTNAME_CLASS.getName()
+ " instances are supported, but found: " + serverName);
}
}
sniHostNames = servernames;
} catch (IllegalAccessException e) {
throw new Error(e);
} catch (InvocationTargetException e) {
throw new Error(e);
}
}
}
}