netty: Implicitly enable Conscrypt when it is available

This implicit loading is more conservative than the loading for
tcnative, as Conscrypt will only be implicitly loaded if there are no
other options. This means the Java 9+ JSSE is preferred over Conscrypt
without explicit user configuration.

While we would generally prefer Conscrypt over JSSE, we want to allow
the user to configure their security providers. There wasn't a good way
to do that with netty-tcnative and the performance of JSSE at the time
was abysmal.

While generally being a good way to allow adopting Conscrypt, this also
allows easily using App Engine Java 8's provided Conscrypt which can
substantially reduce binary size.

See googleapis/google-cloud-java#6425
This commit is contained in:
Eric Anderson 2019-10-03 10:37:17 -07:00
parent e9921b77f2
commit 2caa77d48f
1 changed files with 29 additions and 38 deletions

View File

@ -18,9 +18,9 @@ package io.grpc.netty;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Throwables;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.grpc.ExperimentalApi;
import io.grpc.internal.ConscryptLoader;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
@ -32,8 +32,6 @@ import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays;
@ -95,20 +93,6 @@ public class GrpcSslContexts {
NEXT_PROTOCOL_VERSIONS);
private static final String SUN_PROVIDER_NAME = "SunJSSE";
private static final Method IS_CONSCRYPT_PROVIDER;
static {
Method method = null;
try {
Class<?> conscryptClass = Class.forName("org.conscrypt.Conscrypt");
method = conscryptClass.getMethod("isConscrypt", Provider.class);
} catch (ClassNotFoundException ex) {
logger.log(Level.FINE, "Conscrypt class not found. Not using Conscrypt", ex);
} catch (NoSuchMethodException ex) {
throw new AssertionError(ex);
}
IS_CONSCRYPT_PROVIDER = method;
}
/**
* Creates an SslContextBuilder with ciphers and APN appropriate for gRPC.
@ -223,9 +207,9 @@ public class GrpcSslContexts {
apc = ALPN;
} else {
throw new IllegalArgumentException(
SUN_PROVIDER_NAME + " selected, but Jetty NPN/ALPN unavailable");
SUN_PROVIDER_NAME + " selected, but Java 9+ and Jetty NPN/ALPN unavailable");
}
} else if (isConscrypt(jdkProvider)) {
} else if (ConscryptLoader.isConscrypt(jdkProvider)) {
apc = ALPN;
} else {
throw new IllegalArgumentException("Unknown provider; can't configure: " + jdkProvider);
@ -250,9 +234,11 @@ public class GrpcSslContexts {
logger.log(Level.FINE, "Selecting JDK with provider {0}", provider);
return SslProvider.JDK;
}
logger.log(Level.INFO, "Java 9 ALPN API unavailable (this may be normal)");
logger.log(Level.INFO, "netty-tcnative unavailable (this may be normal)",
OpenSsl.unavailabilityCause());
logger.log(Level.INFO, "Conscrypt not found (this may be normal)");
logger.log(Level.INFO, "Conscrypt not found (this may be normal)",
ConscryptHolder.UNAVAILABILITY_CAUSE);
logger.log(Level.INFO, "Jetty ALPN unavailable (this may be normal)",
JettyTlsUtil.getJettyAlpnUnavailabilityCause());
throw new IllegalStateException(
@ -268,30 +254,16 @@ public class GrpcSslContexts {
|| JettyTlsUtil.isJava9AlpnAvailable()) {
return provider;
}
} else if (isConscrypt(provider)) {
} else if (ConscryptLoader.isConscrypt(provider)) {
return provider;
}
}
if (ConscryptHolder.PROVIDER != null) {
return ConscryptHolder.PROVIDER;
}
return null;
}
private static boolean isConscrypt(Provider provider) {
if (IS_CONSCRYPT_PROVIDER == null) {
return false;
}
try {
return (Boolean) IS_CONSCRYPT_PROVIDER.invoke(null, provider);
} catch (IllegalAccessException ex) {
throw new AssertionError(ex);
} catch (InvocationTargetException ex) {
if (ex.getCause() != null) {
Throwables.throwIfUnchecked(ex.getCause());
// If checked, just wrap up everything.
}
throw new AssertionError(ex);
}
}
@SuppressWarnings("deprecation")
static void ensureAlpnAndH2Enabled(
io.netty.handler.ssl.ApplicationProtocolNegotiator alpnNegotiator) {
@ -304,4 +276,23 @@ public class GrpcSslContexts {
HTTP2_VERSION,
alpnNegotiator.protocols());
}
private static class ConscryptHolder {
static final Provider PROVIDER;
static final Throwable UNAVAILABILITY_CAUSE;
static {
Provider provider;
Throwable cause;
try {
provider = ConscryptLoader.newProvider();
cause = null;
} catch (Throwable t) {
provider = null;
cause = t;
}
PROVIDER = provider;
UNAVAILABILITY_CAUSE = cause;
}
}
}