From be6a5c4e832df440a50f3b02c151b1cf3c3f65fe Mon Sep 17 00:00:00 2001 From: Myk Syr Date: Fri, 17 Apr 2026 15:16:11 +0200 Subject: [PATCH] okhttp: enable TLS 1.3 on Android, retain TLS 1.2-only for desktop JVM The ConnectionSpec used by OkHttpChannelBuilder had TLS 1.3 explicitly disabled since Dec 2020 due to a Conscrypt/SunJSSE incompatibility. However, this incompatibility does not affect Android. The previous code applied the TLS 1.2-only restriction unconditionally to all platforms. Regulatory impact: TLS 1.2 is classified as a legacy mechanism in ENISA Agreed Cryptographic Mechanisms v2.0 (April 2025), with TLS 1.3 listed as the recommended protocol. This limitation has been forcing all downstream components using grpc-okhttp on Android to operate with a legacy protocol, creating compliance friction with the EU Radio Equipment Directive (RED) and EU Cyber Resilience Act (CRA) certification requirements. Fixes: https://github.com/grpc/grpc-java/issues/7431 (Android only) Fixes: https://github.com/grpc/grpc-java/issues/7765 (Android only) --- .../io/grpc/okhttp/OkHttpChannelBuilder.java | 41 +++++++++++++------ .../SslSocketFactoryServerCredentials.java | 2 +- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java index 98f764132fe..ae34b77970b 100644 --- a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java +++ b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java @@ -116,17 +116,26 @@ private enum NegotiationType { CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - - // TLS 1.3 does not work so far. See issues: - // https://github.com/grpc/grpc-java/issues/7765 - // - // TLS 1.3 - //CipherSuite.TLS_AES_128_GCM_SHA256, - //CipherSuite.TLS_AES_256_GCM_SHA384, - //CipherSuite.TLS_CHACHA20_POLY1305_SHA256 - ) - .tlsVersions(/*TlsVersion.TLS_1_3,*/ TlsVersion.TLS_1_2) + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_AES_128_GCM_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_CHACHA20_POLY1305_SHA256) + .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2) + .supportsTlsExtensions(true) + .build(); + + // @VisibleForTesting + static final ConnectionSpec INTERNAL_LEGACY_CONNECTION_SPEC = + new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .cipherSuites( + // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS. + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) + .tlsVersions(TlsVersion.TLS_1_2) .supportsTlsExtensions(true) .build(); @@ -184,7 +193,7 @@ public static OkHttpChannelBuilder forTarget(String target, ChannelCredentials c private SSLSocketFactory sslSocketFactory; private final boolean freezeSecurityConfiguration; private HostnameVerifier hostnameVerifier; - private ConnectionSpec connectionSpec = INTERNAL_DEFAULT_CONNECTION_SPEC; + private ConnectionSpec connectionSpec; private NegotiationType negotiationType = NegotiationType.TLS; private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED; private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS; @@ -199,6 +208,12 @@ public static OkHttpChannelBuilder forTarget(String target, ChannelCredentials c */ private final boolean useGetForSafeMethods = false; + private static ConnectionSpec initialConnectionSpec() { + return (OkHttpProtocolNegotiator.get() instanceof OkHttpProtocolNegotiator.AndroidNegotiator) + ? INTERNAL_DEFAULT_CONNECTION_SPEC + : INTERNAL_LEGACY_CONNECTION_SPEC; + } + private OkHttpChannelBuilder(String host, int port) { this(GrpcUtil.authorityFromHostAndPort(host, port)); } @@ -209,6 +224,7 @@ private OkHttpChannelBuilder(String target) { new OkHttpChannelDefaultPortProvider()); this.freezeSecurityConfiguration = false; this.channelCredentials = null; + this.connectionSpec = initialConnectionSpec(); } OkHttpChannelBuilder( @@ -222,6 +238,7 @@ private OkHttpChannelBuilder(String target) { this.negotiationType = factory == null ? NegotiationType.PLAINTEXT : NegotiationType.TLS; this.freezeSecurityConfiguration = true; this.channelCredentials = channelCreds; + this.connectionSpec = initialConnectionSpec(); } private final class OkHttpChannelTransportFactoryBuilder diff --git a/okhttp/src/main/java/io/grpc/okhttp/SslSocketFactoryServerCredentials.java b/okhttp/src/main/java/io/grpc/okhttp/SslSocketFactoryServerCredentials.java index 63c6f33ff79..ad9af056afc 100644 --- a/okhttp/src/main/java/io/grpc/okhttp/SslSocketFactoryServerCredentials.java +++ b/okhttp/src/main/java/io/grpc/okhttp/SslSocketFactoryServerCredentials.java @@ -41,7 +41,7 @@ static final class ServerCredentials extends io.grpc.ServerCredentials { private final ConnectionSpec connectionSpec; ServerCredentials(SSLSocketFactory factory) { - this(factory, OkHttpChannelBuilder.INTERNAL_DEFAULT_CONNECTION_SPEC); + this(factory, OkHttpChannelBuilder.INTERNAL_LEGACY_CONNECTION_SPEC); } ServerCredentials(SSLSocketFactory factory, ConnectionSpec connectionSpec) {