From df20a1a86b8eab9f05f550b92fdac43a0651faee Mon Sep 17 00:00:00 2001 From: Tigran Mkrtchyan Date: Thu, 6 Nov 2025 18:42:37 +0100 Subject: [PATCH] pool: use SSLTrustManagerWithHostnameChecking to initialize CAnL Motivation: JVM uses X509ExtendedTrustManager class to validate the host certificate. The CANL provides two implementations of TrustManagers: SSLTrustManagerWithHostnameChecking, which extends X509ExtendedTrustManager, and SSLTrustManager, which extends X509TrustManager. In case of the latter one, JDK will wrap with a AbstractTrustManagerWrapper implementation, which enforces additional checks, which are not desired. Modification: Update RemoteHttpTransferService to use SSLTrustManagerWithHostnameChecking to initialize CAnL for remote endpoint certificate validation. Result: TPC-HTTP remote endpoint validation performed based on local trusted store and host name. Fixes: #7927 Acked-by: Karen Hoyos Target: master, 11.1, 11.0, 10.2 Require-book: no Require-notes: yes (cherry picked from commit c483a3ce65bddb5d48eeb3376467ef16e0a3f297) Signed-off-by: Tigran Mkrtchyan --- .../trust/AggregateX509TrustManager.java | 35 +++++++++++++++---- .../trust/AggregateX509TrustManagerTest.java | 9 ++--- .../classic/RemoteHttpTransferService.java | 10 +++--- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/modules/common-security/src/main/java/org/dcache/security/trust/AggregateX509TrustManager.java b/modules/common-security/src/main/java/org/dcache/security/trust/AggregateX509TrustManager.java index 691ad4f17f3..574f3641231 100644 --- a/modules/common-security/src/main/java/org/dcache/security/trust/AggregateX509TrustManager.java +++ b/modules/common-security/src/main/java/org/dcache/security/trust/AggregateX509TrustManager.java @@ -1,6 +1,6 @@ /* dCache - http://www.dcache.org/ * - * Copyright (C) 2021 Deutsches Elektronen-Synchrotron + * Copyright (C) 2021 - 2025 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,28 +19,31 @@ import static java.util.Objects.requireNonNull; +import java.net.Socket; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; /** * Aggregate multiple X509TrustManager instances where a certificate chain is accepted if at least - * one of the X509TrustManager instances accepts it. + * one of the X509ExtendedTrustManager instances accepts it. */ -public class AggregateX509TrustManager implements X509TrustManager { +public class AggregateX509TrustManager extends X509ExtendedTrustManager { - private final List trustManagers; + private final List trustManagers; - public AggregateX509TrustManager(List managers) { + public AggregateX509TrustManager(List managers) { trustManagers = requireNonNull(managers); } @FunctionalInterface private interface CertificateCheck { - void appliedTo(X509TrustManager manager) throws CertificateException; + void appliedTo(X509ExtendedTrustManager manager) throws CertificateException; } private void genericCheck(CertificateCheck check) throws CertificateException { @@ -84,6 +87,26 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) genericCheck(tm -> tm.checkServerTrusted(chain, authType)); } + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + genericCheck(tm -> tm.checkServerTrusted(chain, authType, socket)); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + genericCheck(tm -> tm.checkServerTrusted(chain, authType, socket)); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + genericCheck(tm -> tm.checkServerTrusted(chain, authType, engine)); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + genericCheck(tm -> tm.checkServerTrusted(chain, authType, engine)); + } + @Override public X509Certificate[] getAcceptedIssuers() { return trustManagers.stream() diff --git a/modules/common-security/src/test/java/org/dcache/security/trust/AggregateX509TrustManagerTest.java b/modules/common-security/src/test/java/org/dcache/security/trust/AggregateX509TrustManagerTest.java index a9684c07f10..e16be53c285 100644 --- a/modules/common-security/src/test/java/org/dcache/security/trust/AggregateX509TrustManagerTest.java +++ b/modules/common-security/src/test/java/org/dcache/security/trust/AggregateX509TrustManagerTest.java @@ -1,6 +1,6 @@ /* dCache - http://www.dcache.org/ * - * Copyright (C) 2021 Deutsches Elektronen-Synchrotron + * Copyright (C) 2021 - 2025 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import org.junit.Before; import org.junit.Test; @@ -39,7 +40,7 @@ public class AggregateX509TrustManagerTest { private X509TrustManager manager; - private List inner; + private List inner; @Before public void setup() { @@ -251,7 +252,7 @@ private MockX509TrustManagerBuilder aTrustManager() { */ private static class MockX509TrustManagerBuilder { - private final X509TrustManager manager = mock(X509TrustManager.class); + private final X509ExtendedTrustManager manager = mock(X509ExtendedTrustManager.class); public MockX509TrustManagerBuilder thatFailsClientsWith(CertificateException e) { try { @@ -276,7 +277,7 @@ public MockX509TrustManagerBuilder thatAcceptsIssuers(X509Certificate... issuers return this; } - public X509TrustManager build() { + public X509ExtendedTrustManager build() { return manager; } } diff --git a/modules/dcache/src/main/java/org/dcache/pool/classic/RemoteHttpTransferService.java b/modules/dcache/src/main/java/org/dcache/pool/classic/RemoteHttpTransferService.java index 85734d68b23..f7b815cd20e 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/classic/RemoteHttpTransferService.java +++ b/modules/dcache/src/main/java/org/dcache/pool/classic/RemoteHttpTransferService.java @@ -1,6 +1,6 @@ /* dCache - http://www.dcache.org/ * - * Copyright (C) 2015-2020 Deutsches Elektronen-Synchrotron + * Copyright (C) 2015-2025 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -26,7 +26,8 @@ import eu.emi.security.authn.x509.ProxySupport; import eu.emi.security.authn.x509.RevocationParameters; import eu.emi.security.authn.x509.X509Credential; -import eu.emi.security.authn.x509.helpers.ssl.SSLTrustManager; +import eu.emi.security.authn.x509.helpers.ssl.EnforcingNameMismatchCallback; +import eu.emi.security.authn.x509.helpers.ssl.SSLTrustManagerWithHostnameChecking; import eu.emi.security.authn.x509.impl.OpensslCertChainValidator; import eu.emi.security.authn.x509.impl.ValidatorParams; import java.io.IOException; @@ -45,6 +46,7 @@ import javax.annotation.PostConstruct; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; @@ -205,7 +207,7 @@ protected SSLContext buildSSLContext(@Nullable KeyManager keyManager) return context; } - private X509TrustManager buildTrustManager(Path path) { + private X509ExtendedTrustManager buildTrustManager(Path path) { var ocspParameters = new OCSPParametes(getOcspCheckingMode()); var revocationParams = new RevocationParameters(getCrlCheckingMode(), ocspParameters); var validatorParams = new ValidatorParams(revocationParams, ProxySupport.ALLOW); @@ -214,7 +216,7 @@ private X509TrustManager buildTrustManager(Path path) { var validator = new OpensslCertChainValidator(path.toString(), true, getNamespaceMode(), updateInterval, validatorParams, false); onShutdownTasks.add(validator::dispose); - return new SSLTrustManager(validator); + return new SSLTrustManagerWithHostnameChecking(validator, new EnforcingNameMismatchCallback()); } @Override