From a916b26dac51df2aed45074f9381d1259ace8326 Mon Sep 17 00:00:00 2001 From: Eunbin Son Date: Fri, 28 Aug 2026 18:06:03 +0900 Subject: [PATCH] Fix OkHttpGrpcSender mTLS when using the platform default trust store --- .../okhttp/internal/OkHttpGrpcSender.java | 14 ++++- .../okhttp/internal/OkHttpGrpcSenderTest.java | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/exporters/sender/okhttp/src/main/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSender.java b/exporters/sender/okhttp/src/main/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSender.java index 967ab629c25..48b990cf5a0 100644 --- a/exporters/sender/okhttp/src/main/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSender.java +++ b/exporters/sender/okhttp/src/main/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSender.java @@ -25,6 +25,7 @@ import io.opentelemetry.api.impl.InstrumentationUtil; import io.opentelemetry.exporter.internal.RetryUtil; +import io.opentelemetry.exporter.internal.TlsUtil; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.export.Compressor; import io.opentelemetry.sdk.common.export.GrpcResponse; @@ -50,6 +51,7 @@ import java.util.logging.Logger; import javax.annotation.Nullable; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; import javax.net.ssl.X509TrustManager; import okhttp3.Call; import okhttp3.Callback; @@ -129,8 +131,16 @@ public OkHttpGrpcSender( clientBuilder.protocols(Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE)); } else { clientBuilder.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)); - if (sslContext != null && trustManager != null) { - clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager); + if (sslContext != null) { + X509TrustManager effectiveTrustManager = trustManager; + if (effectiveTrustManager == null) { + try { + effectiveTrustManager = TlsUtil.defaultTrustManager(); + } catch (SSLException e) { + throw new IllegalStateException("Unable to initialize default trust manager", e); + } + } + clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), effectiveTrustManager); } if (enabledProtocols != null && !enabledProtocols.isEmpty()) { TlsVersion[] versions = diff --git a/exporters/sender/okhttp/src/test/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSenderTest.java b/exporters/sender/okhttp/src/test/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSenderTest.java index b5b99877cb2..589fba0ba46 100644 --- a/exporters/sender/okhttp/src/test/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSenderTest.java +++ b/exporters/sender/okhttp/src/test/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpGrpcSenderTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.exporter.sender.okhttp.internal; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -17,6 +19,7 @@ import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; +import java.security.Security; import java.time.Duration; import java.util.Collections; import java.util.Set; @@ -27,6 +30,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; import okhttp3.MediaType; import okhttp3.Protocol; import okhttp3.Request; @@ -305,6 +310,61 @@ void shutdown_InterruptedWhileWaiting_StillSucceeds() throws Exception { "Shutdown should succeed even when interrupted"); } + @Test + void constructor_usesDefaultTrustManagerWhenTrustManagerIsNull() throws Exception { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, null); + + assertThatCode( + () -> + new OkHttpGrpcSender( + "https://localhost", + null, + Duration.ofSeconds(10), + Duration.ofSeconds(10), + Collections::emptyMap, + null, + sslContext, + null, + null, + Long.MAX_VALUE, + null)) + .doesNotThrowAnyException(); + } + + @Test + void constructor_wrapsDefaultTrustManagerFailure() throws Exception { + String originalAlgorithm = Security.getProperty("ssl.TrustManagerFactory.algorithm"); + + try { + Security.setProperty("ssl.TrustManagerFactory.algorithm", "invalid"); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, null); + + assertThatThrownBy( + () -> + new OkHttpGrpcSender( + "https://localhost", + null, + Duration.ofSeconds(10), + Duration.ofSeconds(10), + Collections::emptyMap, + null, + sslContext, + null, + null, + Long.MAX_VALUE, + null)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Unable to initialize default trust manager") + .hasCauseInstanceOf(SSLException.class); + + } finally { + Security.setProperty("ssl.TrustManagerFactory.algorithm", originalAlgorithm); + } + } + /** Simple test marshaler for testing purposes. */ private static class TestMessageWriter implements MessageWriter { @Override