diff options
author | Eric Anderson <ejona@google.com> | 2018-02-15 17:14:10 -0800 |
---|---|---|
committer | Eric Anderson <ejona@google.com> | 2018-03-23 10:05:45 -0700 |
commit | 7eab0d9468ae55bd2b443ae7738f9b8c03f43f26 (patch) | |
tree | b0a908ec379ddc6b6956a0bc1cb53b9770c9782a /netty | |
parent | 0859d480e7c1498f811778d36114d9233345926a (diff) | |
download | grpc-grpc-java-7eab0d9468ae55bd2b443ae7738f9b8c03f43f26.tar.gz |
netty: Add support for Conscrypt
Diffstat (limited to 'netty')
-rw-r--r-- | netty/build.gradle | 3 | ||||
-rw-r--r-- | netty/src/main/java/io/grpc/netty/GrpcSslContexts.java | 155 | ||||
-rw-r--r-- | netty/src/test/java/io/grpc/netty/TlsTest.java | 64 |
3 files changed, 175 insertions, 47 deletions
diff --git a/netty/build.gradle b/netty/build.gradle index dcbffdadc..cc10105fc 100644 --- a/netty/build.gradle +++ b/netty/build.gradle @@ -8,7 +8,8 @@ dependencies { testCompile project(':grpc-core').sourceSets.test.output, project(':grpc-testing'), project(':grpc-testing-proto') - testRuntime libraries.netty_tcnative + testRuntime libraries.netty_tcnative, + libraries.conscrypt signature "org.codehaus.mojo.signature:java17:1.0@signature" } diff --git a/netty/src/main/java/io/grpc/netty/GrpcSslContexts.java b/netty/src/main/java/io/grpc/netty/GrpcSslContexts.java index 4c67ac4eb..15dfb63b9 100644 --- a/netty/src/main/java/io/grpc/netty/GrpcSslContexts.java +++ b/netty/src/main/java/io/grpc/netty/GrpcSslContexts.java @@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.grpc.ExperimentalApi; +import io.grpc.internal.MoreThrowables; import io.netty.handler.codec.http2.Http2SecurityUtil; import io.netty.handler.ssl.ApplicationProtocolConfig; import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; @@ -30,9 +31,15 @@ import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SupportedCipherSuiteFilter; import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.Provider; +import java.security.Security; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Utility for configuring SslContext for gRPC. @@ -40,6 +47,8 @@ import java.util.List; @SuppressWarnings("deprecation") @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1784") public class GrpcSslContexts { + private static final Logger logger = Logger.getLogger(GrpcSslContexts.class.getName()); + private GrpcSslContexts() {} /* @@ -84,6 +93,22 @@ public class GrpcSslContexts { SelectedListenerFailureBehavior.ACCEPT, 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 a SslContextBuilder with ciphers and APN appropriate for gRPC. * @@ -131,49 +156,115 @@ public class GrpcSslContexts { @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1784") @CanIgnoreReturnValue public static SslContextBuilder configure(SslContextBuilder builder, SslProvider provider) { - return builder.sslProvider(provider) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(selectApplicationProtocolConfig(provider)); + switch (provider) { + case JDK: + { + Provider jdkProvider = findJdkProvider(); + if (jdkProvider == null) { + throw new IllegalArgumentException( + "Could not find Jetty NPN/ALPN or Conscrypt as installed JDK providers"); + } + return configure(builder, jdkProvider); + } + case OPENSSL: + { + ApplicationProtocolConfig apc; + if (OpenSsl.isAlpnSupported()) { + apc = NPN_AND_ALPN; + } else { + apc = NPN; + } + return builder + .sslProvider(SslProvider.OPENSSL) + .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) + .applicationProtocolConfig(apc); + } + default: + throw new IllegalArgumentException("Unsupported provider: " + provider); + } } /** - * Returns OpenSSL if available, otherwise returns the JDK provider. + * Set ciphers and APN appropriate for gRPC. Precisely what is set is permitted to change, so if + * an application requires particular settings it should override the options set here. */ - private static SslProvider defaultSslProvider() { - return OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK; + @CanIgnoreReturnValue + public static SslContextBuilder configure(SslContextBuilder builder, Provider jdkProvider) { + ApplicationProtocolConfig apc; + if (SUN_PROVIDER_NAME.equals(jdkProvider.getName())) { + // Jetty ALPN/NPN only supports one of NPN or ALPN + if (JettyTlsUtil.isJettyAlpnConfigured()) { + apc = ALPN; + } else if (JettyTlsUtil.isJettyNpnConfigured()) { + apc = NPN; + } else { + throw new IllegalArgumentException( + SUN_PROVIDER_NAME + " selected, but Jetty NPN/ALPN unavailable"); + } + } else if (isConscrypt(jdkProvider)) { + apc = ALPN; + } else { + throw new IllegalArgumentException("Unknown provider; can't configure: " + jdkProvider); + } + return builder + .sslProvider(SslProvider.JDK) + .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) + .applicationProtocolConfig(apc) + .sslContextProvider(jdkProvider); } /** - * Attempts to select the best {@link ApplicationProtocolConfig} for the given - * {@link SslProvider}. + * Returns OpenSSL if available, otherwise returns the JDK provider. */ - private static ApplicationProtocolConfig selectApplicationProtocolConfig(SslProvider provider) { - switch (provider) { - case JDK: { - if (JettyTlsUtil.isJettyAlpnConfigured()) { - return ALPN; - } - if (JettyTlsUtil.isJettyNpnConfigured()) { - return NPN; - } - if (JettyTlsUtil.isJava9AlpnAvailable()) { - return ALPN; + private static SslProvider defaultSslProvider() { + if (OpenSsl.isAvailable()) { + logger.log(Level.FINE, "Selecting OPENSSL"); + return SslProvider.OPENSSL; + } + Provider provider = findJdkProvider(); + if (provider != null) { + logger.log(Level.FINE, "Selecting JDK with provider {0}", provider); + return SslProvider.JDK; + } + 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, "Jetty ALPN unavailable (this may be normal)", + JettyTlsUtil.getJettyAlpnUnavailabilityCause()); + throw new IllegalStateException( + "Could not find TLS ALPN provider; " + + "no working netty-tcnative, Conscrypt, or Jetty NPN/ALPN available"); + } + + private static Provider findJdkProvider() { + for (Provider provider : Security.getProviders("SSLContext.TLS")) { + if (SUN_PROVIDER_NAME.equals(provider.getName())) { + if (JettyTlsUtil.isJettyAlpnConfigured() + || JettyTlsUtil.isJettyNpnConfigured() + || JettyTlsUtil.isJava9AlpnAvailable()) { + return provider; } - // Use the ALPN cause since it is prefered. - throw new IllegalArgumentException( - "ALPN is not configured properly. See https://github.com/grpc/grpc-java/blob/master/SECURITY.md#troubleshooting" - + " for more information.", - JettyTlsUtil.getJettyAlpnUnavailabilityCause()); + } else if (isConscrypt(provider)) { + return provider; } - case OPENSSL: { - if (!OpenSsl.isAvailable()) { - throw new IllegalArgumentException( - "OpenSSL is not installed on the system.", OpenSsl.unavailabilityCause()); - } - return OpenSsl.isAlpnSupported() ? NPN_AND_ALPN : NPN; + } + 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) { + MoreThrowables.throwIfUnchecked(ex.getCause()); + // If checked, just wrap up everything. } - default: - throw new IllegalArgumentException("Unsupported provider: " + provider); + throw new AssertionError(ex); } } diff --git a/netty/src/test/java/io/grpc/netty/TlsTest.java b/netty/src/test/java/io/grpc/netty/TlsTest.java index 3ee2ba5c1..c99d7f1ce 100644 --- a/netty/src/test/java/io/grpc/netty/TlsTest.java +++ b/netty/src/test/java/io/grpc/netty/TlsTest.java @@ -39,6 +39,8 @@ import io.netty.handler.ssl.SslProvider; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.concurrent.Executors; @@ -48,6 +50,7 @@ import javax.net.ssl.SSLContext; import org.junit.After; import org.junit.Assume; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -61,41 +64,70 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class TlsTest { + public static enum TlsImpl { + TCNATIVE, JETTY, CONSCRYPT; + } + /** * Iterable of various configurations to use for tests. */ @Parameters(name = "{0}") public static Iterable<Object[]> data() { return Arrays.asList(new Object[][] { - {SslProvider.JDK}, {SslProvider.OPENSSL}, + {TlsImpl.TCNATIVE}, {TlsImpl.JETTY}, {TlsImpl.CONSCRYPT}, }); } @Parameter(value = 0) - public SslProvider sslProvider; + public TlsImpl tlsImpl; private ScheduledExecutorService executor; private Server server; private ManagedChannel channel; + private SslProvider sslProvider; + private Provider jdkProvider; private SslContextBuilder clientContextBuilder; + @BeforeClass + public static void loadConscrypt() { + TestUtils.installConscryptIfAvailable(); + } + @Before public void setUp() throws NoSuchAlgorithmException { executor = Executors.newSingleThreadScheduledExecutor(); - if (sslProvider == SslProvider.OPENSSL) { - Assume.assumeTrue(OpenSsl.isAvailable()); + switch (tlsImpl) { + case TCNATIVE: + Assume.assumeTrue(OpenSsl.isAvailable()); + sslProvider = SslProvider.OPENSSL; + break; + case JETTY: + Assume.assumeTrue(Arrays.asList( + SSLContext.getDefault().getSupportedSSLParameters().getCipherSuites()) + .contains("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); + sslProvider = SslProvider.JDK; + jdkProvider = Security.getProvider("SunJSSE"); + Assume.assumeNotNull(jdkProvider); + try { + GrpcSslContexts.configure(SslContextBuilder.forClient(), jdkProvider); + } catch (IllegalArgumentException ex) { + Assume.assumeNoException("Jetty ALPN does not seem available", ex); + } + break; + case CONSCRYPT: + sslProvider = SslProvider.JDK; + jdkProvider = Security.getProvider("Conscrypt"); + Assume.assumeNotNull(jdkProvider); + break; + default: + throw new AssertionError(); } + clientContextBuilder = SslContextBuilder.forClient(); if (sslProvider == SslProvider.JDK) { - Assume.assumeTrue(Arrays.asList( - SSLContext.getDefault().getSupportedSSLParameters().getCipherSuites()) - .contains("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); - try { - GrpcSslContexts.configure(SslContextBuilder.forClient(), SslProvider.JDK); - } catch (IllegalArgumentException ex) { - Assume.assumeNoException("Jetty ALPN does not seem available", ex); - } + GrpcSslContexts.configure(clientContextBuilder, jdkProvider); + } else { + GrpcSslContexts.configure(clientContextBuilder, sslProvider); } - clientContextBuilder = GrpcSslContexts.configure(SslContextBuilder.forClient(), sslProvider); } @After @@ -281,7 +313,11 @@ public class TlsTest { File serverPrivateKeyFile, X509Certificate[] serverTrustedCaCerts) throws IOException { SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(serverCertChainFile, serverPrivateKeyFile); - GrpcSslContexts.configure(sslContextBuilder, sslProvider); + if (sslProvider == SslProvider.JDK) { + GrpcSslContexts.configure(sslContextBuilder, jdkProvider); + } else { + GrpcSslContexts.configure(sslContextBuilder, sslProvider); + } sslContextBuilder.trustManager(serverTrustedCaCerts) .clientAuth(ClientAuth.REQUIRE); |