From 7b68e415e98c22731a5b7905e8973af5501c044b Mon Sep 17 00:00:00 2001 From: Eunbin Son Date: Mon, 10 Aug 2026 09:18:11 +0900 Subject: [PATCH 1/2] Reject mixing keyManager and sslContext in TlsConfigHelper TlsConfigHelper documents that the PEM-based higher level API and the lower level setSslContext are mutually exclusive, but only the trustManager side enforced it. Calling setClientTls followed by setSslContext passed without error and the client certificate was silently dropped, because production senders read getSslContext() and getTrustManager() only. Add the two missing cross-checks so the documented IllegalStateException is thrown in both directions, and extend the existing tests to cover them. --- .../exporter/internal/TlsConfigHelper.java | 6 ++++++ .../exporter/internal/TlsConfigHelperTest.java | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java index 5c2f89e9372..57c4168cbe7 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java @@ -72,6 +72,9 @@ public void setKeyManagerFromCerts(byte[] privateKeyPem, byte[] certificatePem) if (keyManager != null) { throw new IllegalStateException("keyManager has been previously configured"); } + if (sslContext != null) { + throw new IllegalStateException("sslContext has been previously configured"); + } try { keyManager = TlsUtil.keyManager(privateKeyPem, certificatePem); @@ -95,6 +98,9 @@ public void setSslContext(SSLContext sslContext, X509TrustManager trustManager) if (this.sslContext != null || this.trustManager != null) { throw new IllegalStateException("sslContext or trustManager has been previously configured"); } + if (this.keyManager != null) { + throw new IllegalStateException("keyManager has been previously configured"); + } this.trustManager = trustManager; this.sslContext = sslContext; } diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java index 83736d5eb06..21bdedc6c95 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java @@ -66,6 +66,16 @@ void createKeyManager_AlreadyExists_Throws() throws Exception { serverTls.privateKey().getEncoded(), serverTls.certificate().getEncoded())) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("keyManager has been previously configured"); + + helper = new TlsConfigHelper(); + helper.setSslContext( + SSLContext.getInstance("TLS"), TlsUtil.trustManager(serverTls.certificate().getEncoded())); + assertThatThrownBy( + () -> + helper.setKeyManagerFromCerts( + serverTls.privateKey().getEncoded(), serverTls.certificate().getEncoded())) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("sslContext has been previously configured"); } @Test @@ -93,5 +103,12 @@ void setSslContext_AlreadyExists_Throws() throws Exception { assertThatThrownBy(() -> helper.setSslContext(sslContext, trustManager)) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("sslContext or trustManager has been previously configured"); + + helper = new TlsConfigHelper(); + helper.setKeyManagerFromCerts( + serverTls.privateKey().getEncoded(), serverTls.certificate().getEncoded()); + assertThatThrownBy(() -> helper.setSslContext(sslContext, trustManager)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("keyManager has been previously configured"); } } From d5ce900aa77c496700856c9c21571fd0108e8993 Mon Sep 17 00:00:00 2001 From: Eunbin Son Date: Fri, 28 Aug 2026 17:56:00 +0900 Subject: [PATCH 2/2] Reject a configured sslContext in setTrustManagerFromCerts setSslContext also assigns trustManager, so the setSslContext -> setTrustManagerFromCerts case was already rejected by the trustManager check. That check reported "trustManager has been previously configured", which does not name the setter the caller actually used, and it relied on setSslContext requiring a non-null X509TrustManager. Add an explicit sslContext check ahead of the trustManager one so all three setters reject each other the same way, and update the existing test for that path, which pinned the misleading message. --- .../io/opentelemetry/exporter/internal/TlsConfigHelper.java | 3 +++ .../opentelemetry/exporter/internal/TlsConfigHelperTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java index 57c4168cbe7..9ded82943fd 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java @@ -45,6 +45,9 @@ public TlsConfigHelper() {} * @param trustedCertsPem Certificate in PEM format. */ public void setTrustManagerFromCerts(byte[] trustedCertsPem) { + if (sslContext != null) { + throw new IllegalStateException("sslContext has been previously configured"); + } if (trustManager != null) { throw new IllegalStateException("trustManager has been previously configured"); } diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java index 21bdedc6c95..267d13da0da 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java @@ -45,7 +45,7 @@ void createTrustManager_AlreadyExists_Throws() throws Exception { SSLContext.getInstance("TLS"), TlsUtil.trustManager(serverTls.certificate().getEncoded())); assertThatThrownBy(() -> helper.setTrustManagerFromCerts(serverTls.certificate().getEncoded())) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("trustManager has been previously configured"); + .hasMessageContaining("sslContext has been previously configured"); } @Test