[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