From 714d40d879e6c07d717b2437cc23995329ee1af7 Mon Sep 17 00:00:00 2001 From: Bernhard Kraemer Date: Tue, 7 Apr 2026 11:56:19 +0200 Subject: [PATCH] Fix trust anchor matching for cross-signed certificates When a TLS server sends a cross-signed root CA certificate in the chain (e.g. GTS Root R4 signed by GlobalSign), the previous exact byte comparison via .equals() fails to match it against the self-signed version in the truststore, even though both certificates represent the same CA with the same subject and public key. This causes "Trust anchor for certification path not found" errors for servers that include cross-signed root certificates in their TLS chain, which is common with Google Trust Services certificates. Match trust anchors by subject DN + public key instead of exact certificate equality, consistent with RFC 5280 trust anchor identification. --- .../holodeckb2b/security/trust/DefaultCertManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/holodeckb2b-certmanager/src/main/java/org/holodeckb2b/security/trust/DefaultCertManager.java b/modules/holodeckb2b-certmanager/src/main/java/org/holodeckb2b/security/trust/DefaultCertManager.java index f7d74612..bc6bf7d9 100644 --- a/modules/holodeckb2b-certmanager/src/main/java/org/holodeckb2b/security/trust/DefaultCertManager.java +++ b/modules/holodeckb2b-certmanager/src/main/java/org/holodeckb2b/security/trust/DefaultCertManager.java @@ -513,12 +513,16 @@ public IValidationResult validateCertificate(List certs, IValid log.trace("Calculate cert path to validate (i.e. find first trust anchor)"); // We only validate the given certificate path up to the first certificate that is listed as a trust anchor, - // so remove any certificate from the given path that is already in the set of trust anchors + // so remove any certificate from the given path that is already in the set of trust anchors. + // Match by subject + public key instead of exact certificate equality, so that cross-signed + // certificates (same key, different issuer/signature) are recognized as trust anchors. List cpToCheck = new ArrayList<>(); boolean foundAnchor = false; for(int i = 0; !foundAnchor && i < certs.size(); i++) { X509Certificate c = certs.get(i); - if (!(foundAnchor = trustAnchors.parallelStream().anyMatch(a -> a.getTrustedCert().equals(c)))) + if (!(foundAnchor = trustAnchors.parallelStream().anyMatch(a -> + a.getTrustedCert().getSubjectX500Principal().equals(c.getSubjectX500Principal()) && + a.getTrustedCert().getPublicKey().equals(c.getPublicKey())))) cpToCheck.add(c); }