[java-idp-plugin-webauthn] branch main updated: JWEBAUTHN-85 - Allow Multiple Trust Anchors
Codeberg
noreply at shibboleth.net
Mon Oct 12 15:21:16 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-idp-plugin-webauthn.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-webauthn/commit/a065d7ab1a547689d68e96fb146e5d2809e75c6a
The following commit(s) were added to refs/heads/main by this push:
new a065d7a JWEBAUTHN-85 - Allow Multiple Trust Anchors
a065d7a is described below
commit a065d7ab1a547689d68e96fb146e5d2809e75c6a
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Oct 12 17:19:48 2026 +0200
JWEBAUTHN-85 - Allow Multiple Trust Anchors
- Support multiple trust root files and hence multiple trust anchors
https://shibboleth.atlassian.net/browse/JWEBAUTHN-85
---
.../metadata/impl/FidoMetadataServiceFactory.java | 101 +++++++++++++++++++--
.../META-INF/net.shibboleth.idp/postconfig.xml | 3 +-
.../conf/authn/webauthn-metadata.properties | 2 +
3 files changed, 95 insertions(+), 11 deletions(-)
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java
index ebb459c..6622093 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java
@@ -21,9 +21,15 @@ import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.cert.CRL;
import java.security.cert.CRLException;
+import java.security.cert.CertificateException;
+import java.security.cert.TrustAnchor;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -41,6 +47,7 @@ import com.yubico.fido.metadata.FidoMetadataService;
import net.shibboleth.shared.annotation.constraint.Live;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
import net.shibboleth.shared.component.ComponentInitializationException;
@@ -60,8 +67,8 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(FidoMetadataServiceFactory.class);
- /** The file location of the trust root that verifies the downloaded metadata's signature.*/
- @GuardedBy("this") @NonnullAfterInit private Resource trustRootFile;
+ /** The locations of the trust roots that verify the downloaded metadata's signature.*/
+ @GuardedBy("this") @Nonnull @NotLive private List<Resource> trustRootFiles;
/** Where to cache the metadata blob.*/
@GuardedBy("this") @Nullable private Resource cacheFile;
@@ -88,6 +95,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
/** Constructor. */
public FidoMetadataServiceFactory() {
crls = CollectionSupport.emptyList();
+ trustRootFiles = CollectionSupport.emptyList();
verifyDownloadOnly = false;
}
@@ -105,7 +113,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
localMetadataBlobFile.getFilename());
downloader = FidoMetadataDownloader.builder()
.expectLegalHeader(getExpectedLegalHeaders())
- .useTrustRoot(X509Support.decodeCertificate(getTrustRootFile().getFile()))
+ .useTrustRoots(convertTrustRoots(getTrustRootFiles()))
.useBlob(loadMetadataJwt(localMetadataBlobFile))
.useCrls(loadCrls())
.verifyDownloadsOnly(verifyDownloadOnly)
@@ -114,7 +122,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
log.debug("{}: Loading FIDO metadata blob from '{}'", getId(), metadataBlobUrl);
downloader = FidoMetadataDownloader.builder()
.expectLegalHeader(getExpectedLegalHeaders())
- .useTrustRoot(X509Support.decodeCertificate(getTrustRootFile().getFile()))
+ .useTrustRoots(convertTrustRoots(getTrustRootFiles()))
.downloadBlob(localMetadataBlobUrl.getURL())
.useBlobCacheFile(localMetadataCacheFile.getFile())
.useCrls(loadCrls())
@@ -135,6 +143,40 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
throw new FatalBeanException("Can not construct FIDO Metadata service", e);
}
}
+
+ /**
+ * Convert the list of trust root files into Java {@link TrustAnchor}s. Any that fail to be converted throw
+ * a fatal bean exception.
+ *
+ * @param trustRootFiles the list of files to use as trust roots
+ *
+ * @return a set of converted {@link TrustAnchor}s.
+ *
+ * @throws FatalBeanException if a trust root file does not exist or cannot be converted to
+ * a {@link TrustAnchor}
+ *
+ * @since 1.4.3
+ */
+ private Set<TrustAnchor> convertTrustRoots(@Nonnull final List<Resource> trustRootFiles) {
+ final Set<TrustAnchor> anchors = new HashSet<>(trustRootFiles.size());
+ for (final Resource file : trustRootFiles) {
+ if (file == null) {
+ throw new FatalBeanException("Trust anchor file was null");
+ }
+ if (file.exists()) {
+ try {
+ anchors.add(new TrustAnchor(X509Support.decodeCertificate(file.getFile()),null));
+ } catch (CertificateException | IOException e) {
+ throw new FatalBeanException("Unable to decode file '" + file.getFilename() +
+ "' to X509 certificate", e);
+ }
+ } else {
+ throw new FatalBeanException("Unable to convert file '" + file.getFilename() +
+ "' to trust anchor: file does not exist");
+ }
+ }
+ return anchors;
+ }
/**
@@ -178,10 +220,12 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
@Override protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
-
- if (trustRootFile == null) {
- throw new ComponentInitializationException("trustRootFile cannot be null");
+
+ if (trustRootFiles.isEmpty()) {
+ throw new ComponentInitializationException("At least one Trust Root file is required to verify the "
+ + "metadata blob");
}
+
if (metadataBlobUrl == null && metadataBlobFile == null) {
throw new ComponentInitializationException("Metadata blob URL or file must be set");
}
@@ -216,9 +260,12 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
*
* @param file the trust root file
*/
- public synchronized void setTrustRootFile(@Nonnull final Resource file) {
+ public synchronized void setTrustRootFile(@Nullable final Resource file) {
checkSetterPreconditions();
- trustRootFile = Constraint.isNotNull(file, "trustRootCacheFile can not be null");
+ // The file can now be null
+ if (file != null) {
+ setTrustRootFiles(CollectionSupport.listOf(file));
+ }
}
/**
@@ -228,7 +275,41 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
*/
@NonnullAfterInit private synchronized Resource getTrustRootFile() {
checkComponentActive();
- return trustRootFile;
+ return trustRootFiles.get(0);
+ }
+
+ /**
+ * Get the trust root files.
+ *
+ * @return the list of trust root files.
+ *
+ * @since 1.4.3
+ */
+ @Nonnull @NotLive public synchronized List<Resource> getTrustRootFiles() {
+ checkComponentActive();
+ return trustRootFiles;
+ }
+
+ /**
+ * Set the trust roots.
+ *
+ * @param roots the trust roots
+ *
+ * @since 1.4.3
+ */
+ public synchronized void setTrustRootFiles(@Nullable final List<Resource> roots) {
+ checkSetterPreconditions();
+
+ if (roots != null) {
+ trustRootFiles = CollectionSupport.copyToList(roots);
+ if (log.isTraceEnabled()) {
+ log.trace("Setting '{}' trust root file(s): '{}'",trustRootFiles.size(),
+ trustRootFiles.stream().filter(Objects::nonNull)
+ .map(Resource::getDescription)
+ .collect(Collectors.joining(", ")));
+ }
+ }
+
}
/**
diff --git a/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 3cde879..8840acd 100644
--- a/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -117,7 +117,8 @@
<bean id="shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory"
class="net.shibboleth.idp.plugin.authn.webauthn.metadata.impl.FidoMetadataServiceFactory" scope="singleton"
lazy-init="true"
- p:trustRootFile="%{idp.authn.webauthn.metadata.trustRootFile:}"
+ p:trustRootFile="%{idp.authn.webauthn.metadata.trustRootFile:#{null}}"
+ p:trustRootFiles="%{idp.authn.webauthn.metadata.trustRootFiles:#{null}}"
p:cacheFile="%{idp.authn.webauthn.metadata.cacheFile:}"
p:crls="%{idp.authn.webauthn.metadata.crls:#{null}}"
p:metadataBlobUrl="%{idp.authn.webauthn.metadata.metadataBlobUrl:https://mds3.fidoalliance.org}"
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-metadata.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-metadata.properties
index c2f8d94..d42488e 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-metadata.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-metadata.properties
@@ -1,6 +1,8 @@
# Enable FIDO Alliance metadata? If you want to use the metadata service, you *must* download and set the trust root and the legal blob file headers
#idp.authn.webauthn.metadata.enabled = false
#idp.authn.webauthn.metadata.trustRootFile = %{idp.home}/credentials/root-r3.crt
+# A list of trust root files, space separated. Can not be used with the single trustRootFile property.
+#idp.authn.webauthn.metadata.trustRootFiles = %{idp.home}/credentials/root-r3.crt
#idp.authn.webauthn.metadata.expectedLegalHeaders = Retrieval and use of this BLOB indicates acceptance of the appropriate agreement located at https://fidoalliance.org/metadata/metadata-legal-terms/
# Downloaded CRLs to check metadata signature revocation status.
#idp.authn.webauthn.metadata.crls = %{idp.home}/credentials/root-r3.crl
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list