[java-idp-plugin-vci] branch main updated: Rework signing configurations. Credentials and Status List signing have their own Signing Configurations.
Codeberg
noreply at shibboleth.net
Thu Aug 27 10:19:27 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-vci.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/54ff39d0109bf3043b8410c20f65494e5c3b72c4
The following commit(s) were added to refs/heads/main by this push:
new 54ff39d Rework signing configurations. Credentials and Status List signing have their own Signing Configurations.
54ff39d is described below
commit 54ff39d0109bf3043b8410c20f65494e5c3b72c4
Author: Janne Lauros <janne.lauros at csc.fi>
AuthorDate: Thu Aug 27 13:07:17 2026 +0300
Rework signing configurations. Credentials and Status List signing have their own Signing Configurations.
---
.../config/DefaultStatusListConfiguration.java | 59 +++++++++
...ignatureSigningConfigurationLookupFunction.java | 64 +++++++++
.../openidvci/config/OpenIDVCIConfiguration.java | 18 +++
.../impl/AbstractOpenIDVCIConfiguration.java | 40 +++++-
...ignatureSigningConfigurationLookupFunction.java | 145 +++++++++++++++++++++
...thmRestrictedSignatureSigningConfiguration.java | 131 +++++++++++++++++++
.../flows/oauth/status-list/list/list-beans.xml | 18 +--
.../openid/vci/credentials/credentials-beans.xml | 17 ++-
.../idp/service/relying-party/postconfig.xml | 39 +++++-
.../openidvci/conf/openid-vci-credentials.xml | 93 +++++++++++++
.../plugin/openidvci/conf/openid-vci.properties | 5 +
.../shibboleth/plugin/openidvci/module.properties | 7 +
12 files changed, 613 insertions(+), 23 deletions(-)
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/DefaultStatusListConfiguration.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/DefaultStatusListConfiguration.java
index 589edcb..0ed6b15 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/DefaultStatusListConfiguration.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/DefaultStatusListConfiguration.java
@@ -16,10 +16,19 @@
package org.geant.shibboleth.plugin.oauth.statuslist.config;
+import java.util.function.Function;
+
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.idp.profile.config.AbstractInterceptorAwareProfileConfiguration;
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
+import net.shibboleth.shared.annotation.ConfigurationSetting;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
/**
* Profile configuration for the endpoint publishing Status List Tokens, as in
@@ -38,9 +47,59 @@ public class DefaultStatusListConfiguration extends AbstractInterceptorAwareProf
@NotEmpty
public static final String PROFILE_ID = "http://geant.org/ns/profiles/oauth/status-list";
+ /** Signing of Status List Tokens. */
+ @Nonnull
+ private Function<ProfileRequestContext, SignatureSigningConfiguration> statusListSignatureSigningConfigurationLookupStrategy;
+
/** Constructor. */
public DefaultStatusListConfiguration() {
super(PROFILE_ID);
+ statusListSignatureSigningConfigurationLookupStrategy = FunctionSupport.constant(null);
+ }
+
+ /**
+ * Get the {@link SignatureSigningConfiguration} used for signing Status List
+ * Tokens.
+ *
+ * <p>
+ * Status List Token names same issuer as tokens referring to it. While issuer is
+ * derived from signing key, like did:jwk is, this must resolve to same key those
+ * tokens are signed with.
+ * </p>
+ *
+ * @param profileRequestContext current profile request context
+ *
+ * @return the signature signing configuration to use
+ */
+ @ConfigurationSetting(name="statusListSignatureSigningConfiguration")
+ @Nullable
+ public SignatureSigningConfiguration getStatusListSignatureSigningConfiguration(
+ @Nullable final ProfileRequestContext profileRequestContext) {
+ return statusListSignatureSigningConfigurationLookupStrategy.apply(profileRequestContext);
+ }
+
+ /**
+ * Set the {@link SignatureSigningConfiguration} to sign Status List Tokens with.
+ *
+ * @param configuration configuration to use
+ *
+ */
+ public void setStatusListSignatureSigningConfiguration(
+ @Nullable final SignatureSigningConfiguration configuration) {
+ statusListSignatureSigningConfigurationLookupStrategy = FunctionSupport.constant(configuration);
+ }
+
+ /**
+ * Set a lookup strategy for the {@link SignatureSigningConfiguration} to sign
+ * Status List Tokens with.
+ *
+ * @param strategy lookup strategy
+ *
+ */
+ public void setStatusListSignatureSigningConfigurationLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, SignatureSigningConfiguration> strategy) {
+ statusListSignatureSigningConfigurationLookupStrategy = Constraint.isNotNull(strategy,
+ "Lookup strategy cannot be null");
}
}
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/navigate/StatusListSignatureSigningConfigurationLookupFunction.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/navigate/StatusListSignatureSigningConfigurationLookupFunction.java
new file mode 100644
index 0000000..b1b9d76
--- /dev/null
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/oauth/statuslist/config/navigate/StatusListSignatureSigningConfigurationLookupFunction.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025, GÉANT
+ *
+ * Licensed under the Apache License, Version 2.0 (the “License”); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an “AS IS” BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.geant.shibboleth.plugin.oauth.statuslist.config.navigate;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.geant.shibboleth.plugin.oauth.statuslist.config.DefaultStatusListConfiguration;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.shared.collection.CollectionSupport;
+
+/**
+ * A function that obtains
+ * {@link DefaultStatusListConfiguration#getStatusListSignatureSigningConfiguration(ProfileRequestContext)}
+ * if such a profile is available from a {@link RelyingPartyContext} obtained via
+ * a lookup function, by default a child of the {@link ProfileRequestContext}.
+ * The value is returned as a single-valued list.
+ *
+ * <p>
+ * If a specific setting is unavailable, an empty list value is returned.
+ * </p>
+ *
+ */
+public class StatusListSignatureSigningConfigurationLookupFunction
+ extends AbstractRelyingPartyLookupFunction<List<SignatureSigningConfiguration>> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ public List<SignatureSigningConfiguration> apply(@Nullable final ProfileRequestContext input) {
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc instanceof DefaultStatusListConfiguration slpc) {
+ final SignatureSigningConfiguration config =
+ slpc.getStatusListSignatureSigningConfiguration(input);
+ if (config != null) {
+ return CollectionSupport.listOf(config);
+ }
+ }
+ }
+ return CollectionSupport.emptyList();
+ }
+}
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/OpenIDVCIConfiguration.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/OpenIDVCIConfiguration.java
index 745f057..d77411f 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/OpenIDVCIConfiguration.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/OpenIDVCIConfiguration.java
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
import net.shibboleth.oidc.security.jose.SignatureValidationConfiguration;
import net.shibboleth.profile.config.ConditionalProfileConfiguration;
import net.shibboleth.shared.annotation.ConfigurationSetting;
@@ -126,6 +127,23 @@ public interface OpenIDVCIConfiguration extends ConditionalProfileConfiguration
@Nullable SignatureValidationConfiguration getProofSignatureValidationConfiguration(
@Nullable final ProfileRequestContext profileRequestContext);
+ /**
+ * Get the {@link SignatureSigningConfiguration} used for signing issued Credentials.
+ *
+ * <p>
+ * Configuration sets signing keys and algorithms deployment allows at all.
+ * Algorithm is narrowed still by credential_signing_alg_values_supported of
+ * Credential Configuration in issue.
+ * </p>
+ *
+ * @param profileRequestContext current profile request context
+ *
+ * @return the signature signing configuration to use
+ */
+ @ConfigurationSetting(name="credentialSignatureSigningConfiguration")
+ @Nullable SignatureSigningConfiguration getCredentialSignatureSigningConfiguration(
+ @Nullable final ProfileRequestContext profileRequestContext);
+
/**
* Get the {@link ClaimsValidator} to apply to Proof JWTs being validated by this profile.
*
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/AbstractOpenIDVCIConfiguration.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/AbstractOpenIDVCIConfiguration.java
index 9f61f7e..85a0e4b 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/AbstractOpenIDVCIConfiguration.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/AbstractOpenIDVCIConfiguration.java
@@ -18,7 +18,6 @@ package org.geant.shibboleth.plugin.openidvci.config.impl;
import java.time.Duration;
import java.util.function.Function;
-import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -28,12 +27,12 @@ import org.geant.shibboleth.plugin.openidvci.config.impl.stolen.AbstractOIDCSSOC
import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
import net.shibboleth.oidc.security.jose.SignatureValidationConfiguration;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.Positive;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.FunctionSupport;
-import net.shibboleth.shared.logic.PredicateSupport;
public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConfiguration
implements OpenIDVCIConfiguration {
@@ -54,6 +53,10 @@ public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConf
@Nonnull
private Function<ProfileRequestContext, SignatureValidationConfiguration> proofSignatureValidationConfigurationLookupStrategy;
+ /** Signing of issued credentials. */
+ @Nonnull
+ private Function<ProfileRequestContext, SignatureSigningConfiguration> credentialSignatureSigningConfigurationLookupStrategy;
+
/** Validation of JWT claims for proofs. */
@Nonnull
private Function<ProfileRequestContext, ClaimsValidator> proofClaimsValidatorLookupStrategy;
@@ -71,6 +74,7 @@ public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConf
preauthorizedCodeLengthLookupStrategy = FunctionSupport.constant(Integer.valueOf(0));
credentialLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofDays(180));
proofSignatureValidationConfigurationLookupStrategy = FunctionSupport.constant(null);
+ credentialSignatureSigningConfigurationLookupStrategy = FunctionSupport.constant(null);
proofClaimsValidatorLookupStrategy = FunctionSupport.constant(null);
proofNonceGeneratorLookupStrategy = FunctionSupport.constant(null);
}
@@ -181,6 +185,38 @@ public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConf
"Lookup strategy cannot be null");
}
+ /** {@inheritDoc} */
+ @Override
+ @Nullable
+ public SignatureSigningConfiguration getCredentialSignatureSigningConfiguration(
+ @Nullable final ProfileRequestContext profileRequestContext) {
+ return credentialSignatureSigningConfigurationLookupStrategy.apply(profileRequestContext);
+ }
+
+ /**
+ * Set the {@link SignatureSigningConfiguration} to sign issued credentials with.
+ *
+ * @param configuration configuration to use
+ *
+ */
+ public void setCredentialSignatureSigningConfiguration(
+ @Nullable final SignatureSigningConfiguration configuration) {
+ credentialSignatureSigningConfigurationLookupStrategy = FunctionSupport.constant(configuration);
+ }
+
+ /**
+ * Set a lookup strategy for the {@link SignatureSigningConfiguration} to sign
+ * issued credentials with.
+ *
+ * @param strategy lookup strategy
+ *
+ */
+ public void setCredentialSignatureSigningConfigurationLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, SignatureSigningConfiguration> strategy) {
+ credentialSignatureSigningConfigurationLookupStrategy = Constraint.isNotNull(strategy,
+ "Lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
@Nullable
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/navigate/CredentialSignatureSigningConfigurationLookupFunction.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/navigate/CredentialSignatureSigningConfigurationLookupFunction.java
new file mode 100644
index 0000000..89c637e
--- /dev/null
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/navigate/CredentialSignatureSigningConfigurationLookupFunction.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2025, GÉANT
+ *
+ * Licensed under the Apache License, Version 2.0 (the “License”); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an “AS IS” BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.geant.shibboleth.plugin.openidvci.config.navigate;
+
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.geant.shibboleth.plugin.openidvci.config.OpenIDVCIConfiguration;
+import org.geant.shibboleth.plugin.openidvci.credential.CredentialConfiguration;
+import org.geant.shibboleth.plugin.openidvci.messaging.context.CredentialsContext;
+import org.geant.shibboleth.plugin.openidvci.security.jose.AlgorithmRestrictedSignatureSigningConfiguration;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A function that obtains
+ * {@link OpenIDVCIConfiguration#getCredentialSignatureSigningConfiguration(ProfileRequestContext)}
+ * if such a profile is available from a {@link RelyingPartyContext} obtained via
+ * a lookup function, by default a child of the {@link ProfileRequestContext}.
+ * The value is returned as a single-valued list.
+ *
+ * <p>
+ * Configuration is restricted to credential_signing_alg_values_supported of
+ * {@link CredentialConfiguration} in issue. Algorithm belongs to Credential
+ * Configuration, not to requesting client. If Credential Configuration names no
+ * algorithms, configuration is left as it is.
+ * </p>
+ *
+ * <p>
+ * If a specific setting is unavailable, an empty list value is returned.
+ * </p>
+ *
+ */
+public class CredentialSignatureSigningConfigurationLookupFunction
+ extends AbstractRelyingPartyLookupFunction<List<SignatureSigningConfiguration>> {
+
+ /** Class logger. */
+ @Nonnull
+ private Logger log = LoggerFactory.getLogger(CredentialSignatureSigningConfigurationLookupFunction.class);
+
+ /** Strategy used to locate {@link CredentialsContext} to read. */
+ @Nonnull
+ private Function<ProfileRequestContext, CredentialsContext> credentialsContextLookupStrategy;
+
+ /** Constructor. */
+ public CredentialSignatureSigningConfigurationLookupFunction() {
+ final Function<ProfileRequestContext, CredentialsContext> cclcs = new ChildContextLookup<>(
+ CredentialsContext.class).compose(new InboundMessageContextLookup());
+ assert cclcs != null;
+ credentialsContextLookupStrategy = cclcs;
+ }
+
+ /**
+ * Set strategy used to locate the {@link CredentialsContext} to read.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setCredentialsContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, CredentialsContext> strategy) {
+ credentialsContextLookupStrategy = Constraint.isNotNull(strategy,
+ "CredentialsContext lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ public List<SignatureSigningConfiguration> apply(@Nullable final ProfileRequestContext input) {
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc instanceof OpenIDVCIConfiguration vcipc) {
+ final SignatureSigningConfiguration config = vcipc.getCredentialSignatureSigningConfiguration(input);
+ if (config != null) {
+ return CollectionSupport.listOf(restrictToCredentialAlgorithms(config, input));
+ }
+ }
+ }
+ return CollectionSupport.emptyList();
+ }
+
+ /**
+ * Restrict configuration to algorithms Credential Configuration in issue
+ * supports.
+ *
+ * @param configuration configuration to restrict
+ * @param input current profile request context
+ *
+ * @return restricted configuration, or given one if there is nothing to
+ * restrict with
+ */
+ @Nonnull
+ private SignatureSigningConfiguration restrictToCredentialAlgorithms(
+ @Nonnull final SignatureSigningConfiguration configuration,
+ @Nullable final ProfileRequestContext input) {
+
+ final CredentialsContext ctx = credentialsContextLookupStrategy.apply(input);
+ final CredentialConfiguration credentialConfiguration = ctx != null ? ctx.getCredentialConfiguration() : null;
+ if (credentialConfiguration == null) {
+ log.debug("No credential configuration available, signing algorithms not restricted");
+ return configuration;
+ }
+
+ final List<String> supported = credentialConfiguration.getCredentialSigningAlgValuesSupported();
+ if (supported == null || supported.isEmpty()) {
+ log.debug("Credential configuration declares no credential_signing_alg_values_supported, "
+ + "signing algorithms not restricted");
+ return configuration;
+ }
+
+ final SignatureSigningConfiguration restricted =
+ new AlgorithmRestrictedSignatureSigningConfiguration(configuration, supported);
+ if (restricted.getSignatureAlgorithms().isEmpty()) {
+ log.warn("None of the credential_signing_alg_values_supported {} of the credential configuration "
+ + "are offered by the signing configuration", supported);
+ }
+ return restricted;
+ }
+
+}
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/security/jose/AlgorithmRestrictedSignatureSigningConfiguration.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/security/jose/AlgorithmRestrictedSignatureSigningConfiguration.java
new file mode 100644
index 0000000..c684504
--- /dev/null
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/security/jose/AlgorithmRestrictedSignatureSigningConfiguration.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2025, GÉANT
+ *
+ * Licensed under the Apache License, Version 2.0 (the “License”); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an “AS IS” BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.geant.shibboleth.plugin.openidvci.security.jose;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * {@link SignatureSigningConfiguration} offering restricted set of signature
+ * algorithms. Everything else is taken from wrapped configuration.
+ *
+ * <p>
+ * Restricted set is intersection of algorithms of wrapped configuration and ones
+ * asked here, in order of the request. Caller may narrow algorithms of single
+ * message without escaping include/exclude policy or signing keys deployer has
+ * configured.
+ * </p>
+ */
+public class AlgorithmRestrictedSignatureSigningConfiguration implements SignatureSigningConfiguration {
+
+ /** Configuration giving the keys and algorithm policy. */
+ @Nonnull
+ private final SignatureSigningConfiguration wrapped;
+
+ /** Algorithms left after restriction. */
+ @Nonnull
+ @NonnullElements
+ @Unmodifiable
+ @NotLive
+ private final List<String> algorithms;
+
+ /**
+ * Constructor.
+ *
+ * @param configuration configuration to take the rest from
+ * @param restrictTo algorithms to restrict to, in preference order
+ */
+ public AlgorithmRestrictedSignatureSigningConfiguration(
+ @Nonnull final SignatureSigningConfiguration configuration,
+ @Nonnull @NonnullElements final Collection<String> restrictTo) {
+ wrapped = Constraint.isNotNull(configuration, "Signature signing configuration cannot be null");
+ Constraint.isNotNull(restrictTo, "Algorithms to restrict to cannot be null");
+
+ final List<String> offered = configuration.getSignatureAlgorithms();
+ algorithms = CollectionSupport
+ .copyToList(restrictTo.stream().filter(offered::contains).toList());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ @NonnullElements
+ @Unmodifiable
+ @NotLive
+ public List<String> getSignatureAlgorithms() {
+ return algorithms;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ @NonnullElements
+ @Unmodifiable
+ @NotLive
+ public List<Credential> getSigningCredentials() {
+ return wrapped.getSigningCredentials();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ @Unmodifiable
+ @NotLive
+ public Collection<String> getIncludedAlgorithms() {
+ return wrapped.getIncludedAlgorithms();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isIncludeMerge() {
+ return wrapped.isIncludeMerge();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ @Unmodifiable
+ @NotLive
+ public Collection<String> getExcludedAlgorithms() {
+ return wrapped.getExcludedAlgorithms();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isExcludeMerge() {
+ return wrapped.isExcludeMerge();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ public Precedence getIncludeExcludePrecedence() {
+ return wrapped.getIncludeExcludePrecedence();
+ }
+
+}
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth/status-list/list/list-beans.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth/status-list/list/list-beans.xml
index 7e54537..135a6cf 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth/status-list/list/list-beans.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth/status-list/list/list-beans.xml
@@ -22,19 +22,15 @@
c:strategy-ref="shibboleth.MessageContextLookup.Outbound">
<property name="configurationLookupStrategy">
<bean lazy-init="true"
- class="net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction" />
+ class="org.geant.shibboleth.plugin.oauth.statuslist.config.navigate.StatusListSignatureSigningConfigurationLookupFunction" />
</property>
- <!-- CHANGE TO Basic and configure sec conf having correct type of key only-->
- <!-- Security configuration in general needs lot of work still -->
+ <!--
+ Status List Token is signed with issuer's own key. Basic resolver is used, client
+ information aware one would offer client secret as signing credential. There is no
+ client to ask algorithm from either, endpoint is unauthenticated and serves verifiers.
+ -->
<property name="signatureSigningParametersResolver">
- <bean class="net.shibboleth.oidc.security.jose.impl.ClientInformationSignatureSigningParametersResolver">
- <constructor-arg name="signatureAlgorithmLookupStrategy">
- <bean
- class="net.shibboleth.oidc.profile.config.navigate.ClientInformationStringValueLookupFunction"
- c:keyName="id_token_signed_response_alg" />
- </constructor-arg>
- <constructor-arg name="defaultAlgorithmValue" value="ES256" />
- </bean>
+ <bean class="net.shibboleth.oidc.security.jose.impl.BasicSignatureSigningParametersResolver" />
</property>
</bean>
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-beans.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-beans.xml
index 254cc31..80a9570 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-beans.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-beans.xml
@@ -115,17 +115,16 @@
c:strategy-ref="shibboleth.MessageContextLookup.Inbound">
<property name="configurationLookupStrategy">
<bean lazy-init="true"
- class="net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction" />
+ class="org.geant.shibboleth.plugin.openidvci.config.navigate.CredentialSignatureSigningConfigurationLookupFunction" />
</property>
+ <!--
+ Credential is signed with issuer's own key. Basic resolver is used, client
+ information aware one would offer client secret as signing credential. Algorithm
+ comes from Credential Configuration, not from client. OpenID4VCI has no parameter
+ for wallet to ask it.
+ -->
<property name="signatureSigningParametersResolver">
- <bean class="net.shibboleth.oidc.security.jose.impl.ClientInformationSignatureSigningParametersResolver">
- <constructor-arg name="signatureAlgorithmLookupStrategy">
- <bean
- class="net.shibboleth.oidc.profile.config.navigate.ClientInformationStringValueLookupFunction"
- c:keyName="id_token_signed_response_alg" />
- </constructor-arg>
- <constructor-arg name="defaultAlgorithmValue" value="ES256" />
- </bean>
+ <bean class="net.shibboleth.oidc.security.jose.impl.BasicSignatureSigningParametersResolver" />
</property>
<property name="securityParametersContextLookupStrategy">
<bean parent="shibboleth.Functions.Compose"
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 50f4861..7ef642a 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -32,6 +32,7 @@
p:dpopProofSignatureValidationConfiguration="#{getObject('DPoPSignatureValidationConfiguration')}"
p:dpopProofNonceGenerator="#{getObject('DefaultOAuth2DPoPNonceGenerator')}"
p:proofSignatureValidationConfiguration="#{getObject('openidvci.ProofSignatureValidationConfiguration')}"
+ p:credentialSignatureSigningConfiguration="#{getObject('openidvci.SigningConfiguration')}"
p:proofNonceGenerator="#{getObject('openidvci.DefaultOpenIDVCINonceGenerator')}"
p:proofClaimsValidator="#{getObject('openidvci.DefaultProofBodyClaimsValidator')}" />
@@ -49,7 +50,8 @@
<bean id="OAuth.StatusList" lazy-init="true"
class="org.geant.shibboleth.plugin.oauth.statuslist.config.DefaultStatusListConfiguration"
- p:securityConfiguration-ref="%{idp.security.oidc.config:shibboleth.oidc.DefaultSecurityConfiguration}" />
+ p:securityConfiguration-ref="%{idp.security.oidc.config:shibboleth.oidc.DefaultSecurityConfiguration}"
+ p:statusListSignatureSigningConfiguration="#{getObject('oauth.statuslist.SigningConfiguration')}" />
<bean id="OAuth.StatusList.Revoke" parent="AbstractVCIProfile" lazy-init="true"
class="org.geant.shibboleth.plugin.oauth.statuslist.config.DefaultStatusListRevokeConfiguration" />
@@ -96,6 +98,41 @@
</property>
</bean>
+ <!--
+ Signing configuration of issued Credentials. Keys are OP's response signing credentials,
+ unless deployer defines list bean "openidvci.SigningCredentials". Then only those are
+ used. Own key must be published also, see conf/openid-vci-credentials.xml.
+
+ Only EC algorithms are offered, signing actions support nothing else. Used algorithm is
+ narrowed still per Credential Configuration by credential_signing_alg_values_supported.
+ -->
+ <bean id="openidvci.SigningConfiguration"
+ parent="shibboleth.oidc.BasicSignatureSigningConfiguration"
+ p:signingCredentials="#{getObject('openidvci.SigningCredentials') ?: getObject('shibboleth.oidc.SigningCredentialsFactory')}">
+ <property name="signatureAlgorithms">
+ <list>
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_256" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_384" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_512" />
+ </list>
+ </property>
+ </bean>
+
+ <!--
+ Signing configuration of Status List Tokens. Kept separate from Credential one, status
+ list is general purpose mechanism and does not serve only Verifiable Credentials. Keys
+ default still to same ones.
+
+ Status List Token names same issuer as Credentials referring to it. While issuer is
+ did:jwk, issuer IS the signing key. Own key here means Credential refers to status list
+ of some other issuer. Separate the keys only when openidvci.issuer names real issuer.
+ -->
+ <bean id="oauth.statuslist.SigningConfiguration" parent="openidvci.SigningConfiguration"
+ p:signingCredentials="#{getObject('oauth.statuslist.SigningCredentials') ?: getObject('openidvci.SigningCredentials') ?: getObject('shibboleth.oidc.SigningCredentialsFactory')}" />
+
<bean id="openidvci.ProofSignatureValidationConfiguration"
parent="shibboleth.oidc.BasicSignatureValidationConfiguration"
p:signatureTrustEngine-ref="openidvci.TokenKeyTrustEngineForProofJWT"/>
diff --git a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci-credentials.xml b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci-credentials.xml
new file mode 100644
index 0000000..1f72c13
--- /dev/null
+++ b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci-credentials.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <!--
+ This file defines with which key issued Verifiable Credentials are signed. This file
+ should be imported into credentials.xml.
+
+ Nothing here is active by default. Credentials are signed with OP's own response
+ signing credentials, the ones in shibboleth.oidc.SigningCredentials. Keeping it that
+ way needs nothing to be done.
+
+ Uncomment beans below to sign Credentials with own key instead. Note that Credential
+ is verified long after issuing it, by wallet or verifier having no relation to your
+ OP. Own key is not private matter between your deployment and its clients. See
+ "Publishing" below.
+ -->
+
+ <!--
+ Signing key of Verifiable Credentials only. Must be EC key, signing actions support
+ nothing else, and it should match one of algorithms you advertise in
+ credential_signing_alg_values_supported of your Credential Issuer metadata. Generate
+ one with bin/jwtgen.sh, same way as OP's own keys.
+ -->
+ <!--
+ <bean id="openidvci.DefaultESSigningCredential" parent="shibboleth.JWKCredential"
+ p:resource="%{idp.signing.openidvci.es.key}" />
+ -->
+
+ <!--
+ Lists ALL credentials used for signing issued Verifiable Credentials. Defining this
+ list REPLACES OP's credentials in Credential signing, it does not add to them. So list
+ every key you want to be considered. First one compatible with used algorithm is the
+ one signing.
+
+ Leave this undefined to keep sharing OP's signing credentials.
+ -->
+ <!--
+ <util:list id="openidvci.SigningCredentials">
+ <ref bean="openidvci.DefaultESSigningCredential" />
+ </util:list>
+ -->
+
+ <!--
+ Status List Tokens are signed separately from Credentials. Status list is general
+ purpose mechanism and does not serve only Verifiable Credentials. By default they are
+ signed with same key as Credentials, so nothing needs to be done here.
+
+ Define this list ONLY if status list must be signed with own key. Status List Token
+ names same issuer as Credentials referring to it. While that issuer is did:jwk, issuer
+ IS the key: separating them makes your Credentials refer to status list that looks like
+ issued by somebody else. Separate them only when openidvci.issuer names real issuer.
+ -->
+ <!--
+ <util:list id="oauth.statuslist.SigningCredentials">
+ <ref bean="openidvci.DefaultESSigningCredential" />
+ </util:list>
+ -->
+
+ <!--
+ Publishing.
+
+ Wallet or verifier finds your public key from key set OP publishes in its jwks_uri.
+ That key set has shibboleth.oidc.SigningCredentials in it, so key defined above is NOT
+ published. Credentials signed with it CANNOT be verified before you say otherwise.
+
+ To publish it, define shibboleth.oidc.SigningCredentialsToPublish in oidc-credentials.xml.
+ That bean REPLACES published set also, it does not add to it. So it must list OP's own
+ signing credentials too, or clients can not verify id tokens anymore. List looks like
+ this:
+
+ <util:list id="shibboleth.oidc.SigningCredentialsToPublish">
+ <ref bean="shibboleth.oidc.DefaultRSSigningCredential" />
+ <ref bean="shibboleth.oidc.DefaultESSigningCredential" />
+ <ref bean="openidvci.DefaultESSigningCredential" />
+ </util:list>
+
+ Note that same list is also set of keys trusted in validation of JWTs your deployment
+ has issued to itself, logout tokens for example. Credential signing key becomes trusted
+ there too.
+
+ Publish the key before you start signing with it. Verifier having fetched key set
+ earlier may still have old one in cache.
+ -->
+
+</beans>
diff --git a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
index d52d02a..fe0350a 100644
--- a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
+++ b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
@@ -2,3 +2,8 @@ oauth.statuslist.indexPermutationKey=change_this_to_secret
oauth.statuslist.ttl=PT1H
oauth.statuslist.assignmentRetention=P7D
openidvci.issuer=
+
+# Signing key of issued Verifiable Credentials. Used only if you enable credential of same
+# name in conf/openid-vci-credentials.xml. Until that, Credentials are signed with OP's own
+# signing credentials. Must be EC key.
+idp.signing.openidvci.es.key = %{idp.home}/credentials/openid-vci-signing-es.jwk
diff --git a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/module.properties b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/module.properties
index 8a2e099..f65119b 100644
--- a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/module.properties
+++ b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/module.properties
@@ -12,3 +12,10 @@ OpenID-VCI.url = https://github.com/CSCfi/shibboleth-idp-oauth2-deviceflow-exten
OpenID-VCI.1.src = /org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
OpenID-VCI.1.dest = conf/openid-vci.properties
+
+OpenID-VCI.2.src = /org/geant/shibboleth/plugin/openidvci/conf/openid-vci-credentials.xml
+OpenID-VCI.2.dest = conf/openid-vci-credentials.xml
+
+OpenID-VCI.postenable = Add <import resource="openid-vci-credentials.xml" /> in conf/credentials.xml \
+if you want to sign Verifiable Credentials with own key. Until that, Credentials are signed with \
+OP's own signing credentials.
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list