[java-idp-plugin-webauthn] branch main updated: Add downloaded CRL support and example CRL files

Phil Smart philip.smart at jisc.ac.uk
Wed Apr 10 16:08:38 UTC 2024


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-webauthn.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-webauthn.git;a=commit;h=ac5a40d76a69f8809ca3555c0483fcdf8810204b

The following commit(s) were added to refs/heads/main by this push:
     new ac5a40d  Add downloaded CRL support and example CRL files
ac5a40d is described below

commit ac5a40d76a69f8809ca3555c0483fcdf8810204b
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Apr 10 17:08:35 2024 +0100

    Add downloaded CRL support and example CRL files
    
     - Allows the metadata to load without having to enable the CRL DP JVM
    setting.
     - The example files are meaningless for now. The revocation checks will
    always succeed (unless somehow an old revoked cert was used).
     - Going forward, there should be ways to enable CRL DP revocation
    checking just on the Fido downloader (from the Yibco side).
---
 .../authn/webauthn/authn/RegistrationResult.java   |  35 +++++-----
 .../ValidateAuthenticatorAttestationResponse.java  |   2 +-
 .../metadata/FidoMetadataServiceFactory.java       |  72 +++++++++++++++++++--
 .../META-INF/net.shibboleth.idp/postconfig.xml     |   1 +
 .../authn/webauthn/conf/authn/webauthn.properties  |   3 +
 .../authn/webauthn/crls/gsextendvalsha2g3r3.crl    | Bin 0 -> 8026 bytes
 .../idp/plugin/authn/webauthn/crls/root-r3.crl     | Bin 0 -> 2157 bytes
 7 files changed, 88 insertions(+), 25 deletions(-)

diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java
index 3d6f915..ca74b56 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java
@@ -59,7 +59,11 @@ public class RegistrationResult {
     @Nonnull private final 
     PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> credential;
 
-    
+    /**
+     * Private constructor for use by the builder.
+     *
+     * @param builder the builder used to set the fields of this class
+     */
     private RegistrationResult(final Builder builder) {
         this.attestationTrusted = builder.attestationTrusted;
         this.attestationType = builder.attestationType;
@@ -71,7 +75,7 @@ public class RegistrationResult {
      * 
      * @return the builder
      */
-    public static Builder builder() {
+    @Nonnull public static Builder builder() {
         return new Builder();
     }    
 
@@ -93,7 +97,7 @@ public class RegistrationResult {
      * 
      * @return the attestation type.
      */
-    public final AttestationType getAttestationType() {
+    @Nonnull public final AttestationType getAttestationType() {
         return attestationType;
     }
     
@@ -102,12 +106,14 @@ public class RegistrationResult {
      * 
      * @return the credential ID and transports of the created credential.
      */
-    public PublicKeyCredentialDescriptor getKeyId() {
-        return PublicKeyCredentialDescriptor.builder()
+    @Nonnull public PublicKeyCredentialDescriptor getKeyId() {
+        final PublicKeyCredentialDescriptor descriptor = PublicKeyCredentialDescriptor.builder()
                 .id(credential.getId())
                 .type(credential.getType())
                 .transports(credential.getResponse().getTransports())
                 .build();
+        assert descriptor != null;
+        return descriptor;
     }
 
     /**
@@ -143,16 +149,14 @@ public class RegistrationResult {
         if (attestedCredentialData.isPresent()) {
             return attestedCredentialData.get().getAaguid();
         }
-        return null;
-        
-    }
-    
+        return null;        
+    }    
 
     /**
      * Try to determine if this credential is a discoverable (passkey) type by inspecting the ResidentKey flag inside
      * the credential properties extension. 
      * 
-     * @return true if the credential is disoverable (a passkey), false if it is not, or empty if not know e.g. 
+     * @return true if the credential is discoverable (a passkey), false if it is not, or empty if not known e.g. 
      *          the extensions were not returned.
      */
     @Nonnull public Optional<Boolean> isDiscoverable() {
@@ -165,7 +169,7 @@ public class RegistrationResult {
 
 
     /**
-     * Was the user verified by a suitable authorization guester during the registration ceremony? 
+     * Was the user verified by a suitable authorization gesture during the registration ceremony? 
      * 
      * @return true if the authenticator claims to have performed user verification, false otherwise.
      */
@@ -179,8 +183,8 @@ public class RegistrationResult {
      * 
      * @return the credential
      */
-    public final 
-    PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> getCredential() {
+    @Nonnull public final 
+        PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> getCredential() {
         return credential;
     }
 
@@ -213,9 +217,4 @@ public class RegistrationResult {
             return new RegistrationResult(this);
         }
     }
-
-
-
-
-    
 }
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
index b01436c..e98fc8f 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
@@ -87,7 +87,7 @@ public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnRe
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final WebAuthnRegistrationContext context) {
 
-        try {        
+        try {     
             final RegistrationResult credentialPublicKey = 
                 getWebAuthnClient().validateAuthenticatorAttestationResponse(pkCredCreationOptions, attestation);
             
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/FidoMetadataServiceFactory.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/FidoMetadataServiceFactory.java
index 83cd3b0..1019a64 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/FidoMetadataServiceFactory.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/FidoMetadataServiceFactory.java
@@ -16,7 +16,13 @@ package net.shibboleth.idp.plugin.authn.webauthn.metadata;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -31,7 +37,9 @@ import org.springframework.core.io.Resource;
 import com.yubico.fido.metadata.FidoMetadataDownloader;
 import com.yubico.fido.metadata.FidoMetadataService;
 
+import net.shibboleth.shared.annotation.constraint.Live;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
@@ -39,7 +47,8 @@ import net.shibboleth.shared.primitive.LoggerFactory;
 
 
 /**
- * Spring factory bean for creating a {@link FidoMetadataService}.
+ * Spring factory bean for creating a {@link FidoMetadataService}. Only downloads or loads FIDO metadata on
+ * creation.
  */
 public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializableComponent 
         implements FactoryBean<FidoMetadataService> {
@@ -61,6 +70,14 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
     
     /** The expected set of legal headers on the FIDO metadata blob.*/
     @GuardedBy("this") @NonnullAfterInit private String[] expectedLegalHeaders;
+    
+    /** A List of CRLs to check the revocation status of the metadata signature.*/
+    @GuardedBy("this") @Nonnull private List<Resource> crls;
+    
+    /** Constructor. */
+    public FidoMetadataServiceFactory() {
+        crls = CollectionSupport.emptyList();
+    }
 
     /** {@inheritDoc} */
     @Override
@@ -77,6 +94,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
                 .expectLegalHeader(getExpectedLegalHeaders())
                 .useTrustRoot(X509Support.decodeCertificate(getTrustRootFile().getFile()))
                 .useBlob(loadMetadataJwt(localMetadataBlobFile))
+                .useCrls(loadCrls())
                 .build();
         } else if (localMetadataBlobUrl != null && localMetadataCacheFile != null){
             log.debug("{}: Loading FIDO metadata blob from '{}'", getId(), metadataBlobUrl);
@@ -85,6 +103,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
             .useTrustRoot(X509Support.decodeCertificate(getTrustRootFile().getFile()))
             .downloadBlob(localMetadataBlobUrl.getURL())
             .useBlobCacheFile(localMetadataCacheFile.getFile())
+            .useCrls(loadCrls())
             .verifyDownloadsOnly(true)
             .build();
         } else {
@@ -104,6 +123,28 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
     }
 
     
+    /**
+     * Load CRLs from the given set of CRL resources into a collection of CRL objects.
+     * 
+     * @return the loaded CRL collection
+     */
+    @Nonnull private Collection<CRL> loadCrls() {
+        final List<Resource> localCrls = getCrls();
+        if (localCrls.isEmpty()) {
+            return CollectionSupport.emptyList();
+        }
+        final List<CRL> crlsConverted = new ArrayList<>(localCrls.size());
+        for (final Resource crlFile : localCrls) {
+            try(final InputStream is = crlFile.getInputStream())  {
+                crlsConverted.addAll(X509Support.decodeCRLs(is));
+            } catch (final CRLException | IOException e) {
+                log.error("Could not decode CRL file at {}: {}", crlFile.getDescription(), e.getMessage());
+                throw new FatalBeanException("Could not decode provided CRL file " + crlFile.getDescription(), e);
+            }
+        }
+        return crlsConverted;
+    }
+
     /**
      * Load the given metadata blob file into a String. 
      * 
@@ -148,7 +189,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
      * 
      * @return where to cache the metadata blob.
      */
-    @Nullable public synchronized Resource getCacheFile() {
+    @Nullable private synchronized Resource getCacheFile() {
         checkComponentActive();
         return cacheFile;
     }
@@ -168,7 +209,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
      * 
      * @return the trust root file
      */
-    @NonnullAfterInit public synchronized Resource getTrustRootFile() {
+    @NonnullAfterInit private synchronized Resource getTrustRootFile() {
         checkComponentActive();
         return trustRootFile;
     }
@@ -178,7 +219,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
      * 
      * @return the metadata blob file URL.
      */
-    @Nullable public synchronized Resource getMetadataBlobUrl() {
+    @Nullable private synchronized Resource getMetadataBlobUrl() {
         checkComponentActive();
         return metadataBlobUrl;
     }
@@ -209,7 +250,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
      * 
      * @return the metadata blob file.
      */
-    @Nullable public synchronized Resource getMetadataBlobFile() {
+    @Nullable private synchronized Resource getMetadataBlobFile() {
         return metadataBlobFile;
     }
     
@@ -228,9 +269,28 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
      * 
      * @return the expected legal headers.
      */
-    @NonnullAfterInit public synchronized String[] getExpectedLegalHeaders() {
+    @NonnullAfterInit private synchronized String[] getExpectedLegalHeaders() {
         return expectedLegalHeaders;
     }
+    
+    /**
+     * Set the CRLs to use to check the revocation status of the metadata signature.
+     * 
+     * @param revocationLists The crls to set.
+     */
+    public synchronized void setCrls(@Nonnull final List<Resource> revocationLists) {
+        checkSetterPreconditions();
+        crls = Constraint.isNotNull(revocationLists, "CRL list can not be null");
+    }
+    
+    /**
+     * Get the CRLs to use to check the revocation status of the metadata signature.
+     * 
+     * @return the crls.
+     */
+    @Nonnull @Live private synchronized List<Resource> getCrls() {
+        return crls;
+    }
 
     /** {@inheritDoc} */
     @Override
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 5de6433..efda6ff 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
@@ -93,6 +93,7 @@
         lazy-init="true"
         p:trustRootFile="%{idp.authn.webauthn.metadata.trustRootFile:}"
         p:cacheFile="%{idp.authn.webauthn.metadata.cacheFile:}"
+        p:crls="%{idp.authn.webauthn.metadata.crls}"
         p:metadataBlobUrl="%{idp.authn.webauthn.metadata.metadataBlobUrl:https://mds3.fidoalliance.org}"
         p:metadataBlobFile="%{idp.authn.webauthn.metadata.metadataBlobFile:}"
         p:expectedLegalHeaders="%{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/}"/>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
index e7221fe..60ac8a4 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
@@ -45,6 +45,9 @@ idp.authn.webauthn.supportedPrincipals = \
 #idp.authn.webauthn.metadata.enabled = false
 #idp.authn.webauthn.metadata.trustRootFile =
 #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. The example set of CRLs given provide no function other than to pass the revocation checks, define your own in production
+idp.authn.webauthn.metadata.crls = classpath:/net/shibboleth/idp/plugin/authn/webauthn/crls/gsextendvalsha2g3r3.crl, classpath:/net/shibboleth/idp/plugin/authn/webauthn/crls/root-r3.crl
+
 
 ## If you want to download the metadata from a URL when the IdP starts, you must specify a cache file (.bin) and the URL to fetch the metadata
 #idp.authn.webauthn.metadata.cacheFile = 
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/crls/gsextendvalsha2g3r3.crl b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/crls/gsextendvalsha2g3r3.crl
new file mode 100644
index 0000000..d2e3992
Binary files /dev/null and b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/crls/gsextendvalsha2g3r3.crl differ
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/crls/root-r3.crl b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/crls/root-r3.crl
new file mode 100644
index 0000000..ef7074b
Binary files /dev/null and b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/crls/root-r3.crl differ

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list