[java-idp-plugin-vci] 01/01: Verify signatures of all proofs
Codeberg
noreply at shibboleth.net
Tue Apr 7 13:35:35 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch dev/PROOF
in repository java-idp-plugin-vci.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/bc31d774a80555eeb84dccdf6db3f135eb02296c
commit bc31d774a80555eeb84dccdf6db3f135eb02296c
Author: Janne Lauros <janne.lauros at csc.fi>
AuthorDate: Tue Apr 7 16:35:10 2026 +0300
Verify signatures of all proofs
---
openid-vci-impl/pom.xml | 4 +
.../openidvci/config/OpenIDVCIConfiguration.java | 12 ++
.../impl/AbstractOpenIDVCIConfiguration.java | 39 ++++++
...atureValidationConfigurationLookupFunction.java | 63 +++++++++
.../messaging/context/CredentialsContext.java | 8 +-
.../plugin/openidvci/profile/impl/ParseProof.java | 8 +-
.../JWTMessageSignaturesSecurityHandler.java | 151 +++++++++++++++++++++
.../openid/vci/credentials/credentials-beans.xml | 65 ++++++++-
.../openid/vci/credentials/credentials-flow.xml | 2 +
.../idp/service/relying-party/postconfig.xml | 11 +-
10 files changed, 354 insertions(+), 9 deletions(-)
diff --git a/openid-vci-impl/pom.xml b/openid-vci-impl/pom.xml
index d26aa45..f94b128 100644
--- a/openid-vci-impl/pom.xml
+++ b/openid-vci-impl/pom.xml
@@ -55,6 +55,10 @@
<artifactId>oidc-common-crypto-impl</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-saml-impl</artifactId>
+ </dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
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 c4baa4e..e028dd6 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
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
+import net.shibboleth.oidc.security.jose.SignatureValidationConfiguration;
import net.shibboleth.profile.config.ConditionalProfileConfiguration;
import net.shibboleth.shared.annotation.ConfigurationSetting;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
@@ -111,5 +112,16 @@ public interface OpenIDVCIConfiguration extends ConditionalProfileConfiguration
@Positive
@Nonnull
Duration getCredentialLifetime(@Nullable final ProfileRequestContext profileRequestContext);
+
+ /**
+ * Get the {@link SignatureValidationConfiguration} to be used for Credential Proof JWT signature validation.
+ *
+ * @param profileRequestContext current profile request context
+ *
+ * @return the signature validation configuration to use
+ */
+ @ConfigurationSetting(name="proofSignatureValidationConfiguration")
+ @Nullable SignatureValidationConfiguration getProofSignatureValidationConfiguration(
+ @Nullable final ProfileRequestContext profileRequestContext);
}
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 41abd74..7d5d482 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
@@ -26,6 +26,7 @@ import org.geant.shibboleth.plugin.openidvci.config.OpenIDVCIConfiguration;
import org.geant.shibboleth.plugin.openidvci.config.impl.stolen.AbstractOIDCSSOConfiguration;
import org.opensaml.profile.context.ProfileRequestContext;
+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;
@@ -45,6 +46,10 @@ public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConf
/** Lookup function to supply credential lifetime. */
@Nonnull
private Function<ProfileRequestContext, Duration> credentialLifetimeLookupStrategy;
+
+ /** Validation of JWT signature of proofs. */
+ @Nonnull
+ private Function<ProfileRequestContext, SignatureValidationConfiguration> proofSignatureValidationConfigurationLookupStrategy;
/**
* Constructor.
@@ -54,6 +59,7 @@ public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConf
preauthorizedCodeLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofMinutes(10));
preauthorizedCodeLengthLookupStrategy = FunctionSupport.constant(Integer.valueOf(0));
credentialLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofDays(180));
+ proofSignatureValidationConfigurationLookupStrategy = FunctionSupport.constant(null);
}
/** {@inheritDoc} */
@@ -128,5 +134,38 @@ public abstract class AbstractOpenIDVCIConfiguration extends AbstractOIDCSSOConf
credentialLifetimeLookupStrategy = FunctionSupport.constant(lifetime);
}
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable
+ public SignatureValidationConfiguration getProofSignatureValidationConfiguration(
+ @Nullable final ProfileRequestContext profileRequestContext) {
+ return proofSignatureValidationConfigurationLookupStrategy.apply(profileRequestContext);
+ }
+
+ /**
+ * Set the {@link SignatureValidationConfiguration} to validate the Proof
+ * JWT signatures.
+ *
+ * @param configuration configuration to use
+ *
+ */
+ public void setProofSignatureValidationConfiguration(
+ @Nullable final SignatureValidationConfiguration configuration) {
+ proofSignatureValidationConfigurationLookupStrategy = FunctionSupport.constant(configuration);
+ }
+
+ /**
+ * Set a lookup strategy for the {@link SignatureValidationConfiguration} to
+ * validate the Proof JWT signatures.
+ *
+ * @param strategy lookup strategy
+ *
+ */
+ public void setProofSignatureValidationConfigurationLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, SignatureValidationConfiguration> strategy) {
+ proofSignatureValidationConfigurationLookupStrategy = Constraint.isNotNull(strategy,
+ "Lookup strategy cannot be null");
+ }
}
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/navigate/ProofSignatureValidationConfigurationLookupFunction.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/navigate/ProofSignatureValidationConfigurationLookupFunction.java
new file mode 100644
index 0000000..9f6b25a
--- /dev/null
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/navigate/ProofSignatureValidationConfigurationLookupFunction.java
@@ -0,0 +1,63 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.geant.shibboleth.plugin.openidvci.config.OpenIDVCIConfiguration;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+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.oidc.security.jose.SignatureValidationConfiguration;
+
+/**
+ * A function that obtains
+ * {@link OpenIDVCIConfiguration#getProofSignatureValidationConfiguration(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 ProofSignatureValidationConfigurationLookupFunction
+ extends AbstractRelyingPartyLookupFunction<List<SignatureValidationConfiguration>> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull
+ public List<SignatureValidationConfiguration> 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 SignatureValidationConfiguration config = vcipc.getProofSignatureValidationConfiguration(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/messaging/context/CredentialsContext.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/messaging/context/CredentialsContext.java
index 4f50726..2e5edca 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/messaging/context/CredentialsContext.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/messaging/context/CredentialsContext.java
@@ -25,8 +25,8 @@ import org.geant.shibboleth.plugin.openidvci.credential.CredentialConfiguration;
import org.geant.shibboleth.plugin.openidvci.messaging.impl.CredentialOfferRequestedCredential;
import org.opensaml.messaging.context.BaseContext;
-import com.nimbusds.jose.JWSObject;
import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
/**
@@ -47,7 +47,7 @@ public class CredentialsContext extends BaseContext {
/** Validated proofs of wallet. */
@Nullable
- private List<JWSObject> proofs;
+ private List<SignedJWT> proofs;
/** Credential shell(s) per proof. */
@Nullable
@@ -71,7 +71,7 @@ public class CredentialsContext extends BaseContext {
* @return Validated proofs of wallet
*/
@Nullable
- public List<JWSObject> getProofs() {
+ public List<SignedJWT> getProofs() {
return proofs;
}
@@ -80,7 +80,7 @@ public class CredentialsContext extends BaseContext {
*
* @param proofs Validated proofs of wallet
*/
- public void setProofs(@Nullable List<JWSObject> proofs) {
+ public void setProofs(@Nullable List<SignedJWT> proofs) {
this.proofs = proofs;
}
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/ParseProof.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/ParseProof.java
index 60344bc..048f4ed 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/ParseProof.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/ParseProof.java
@@ -37,6 +37,7 @@ import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
@@ -128,12 +129,12 @@ public class ParseProof extends AbstractProfileAction {
ActionSupport.buildEvent(profileRequestContext, OpenIDVCIEventIds.PROOF_TYPE_UNSUPPORTED);
return;
}
- List<JWSObject> proofs = new ArrayList<JWSObject>();
+ List<SignedJWT> proofs = new ArrayList<SignedJWT>();
if (proofToken instanceof List<?> tokens) {
tokens.forEach(token -> {
if (token instanceof String strToken) {
try {
- JWSObject singleProof = JWSObject.parse(strToken);
+ SignedJWT singleProof = SignedJWT.parse(strToken);
validateJWTProof(singleProof, profileRequestContext);
proofs.add(singleProof);
} catch (Exception e) {
@@ -167,7 +168,8 @@ public class ParseProof extends AbstractProfileAction {
throw new Exception("typ should be openid4vci-proof+jwt: " + proof.getHeader().getType());
}
// TBD validate header parameters kid, jwk, x5c, key_attestation and trust_chain
- // TBD validate signature
+ // Further processing will then verify the signature. TBD add support also for
+ // other than jwk case.
if (validator != null) {
// TBD validate body parameters iss, aud, iat and nonce
validator.validate(JWTClaimsSet.parse(proof.getPayload().toJSONObject()), profileRequestContext);
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/security/JWTMessageSignaturesSecurityHandler.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/security/JWTMessageSignaturesSecurityHandler.java
new file mode 100644
index 0000000..5223444
--- /dev/null
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/security/JWTMessageSignaturesSecurityHandler.java
@@ -0,0 +1,151 @@
+/*
+ * 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;
+
+import java.text.ParseException;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.security.trust.TrustEngine;
+import org.slf4j.Logger;
+
+import com.nimbusds.jose.JWSObject.State;
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
+import net.shibboleth.oidc.security.impl.BaseJWTSignatureSecurityHandler;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A {@link MessageHandler} that uses a {@link TrustEngine} to evaluate the
+ * signatures of signed JWTs.
+ *
+ * <p>
+ * Note, if the JWT is not in a signed state an exception will be thrown i.e.
+ * JWTs must be signed by the time this handler executes.
+ * </p>
+ *
+ * <p>
+ * Also, the JWT must also have claims, otherwise an exception is throw. This
+ * restriction could be lifted if it is feasible to check the signature of a JWT
+ * with a null payload - even if pointless?
+ * </p>
+ */
+public class JWTMessageSignaturesSecurityHandler extends BaseJWTSignatureSecurityHandler {
+
+ /** Logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(JWTMessageSignaturesSecurityHandler.class);
+
+ /**
+ * Function that looks up signed JWTs from the given message context to
+ * validate.
+ */
+ @NonnullAfterInit
+ private Function<MessageContext, List<SignedJWT>> jwtTokenLookupStrategy;
+
+ /** The extracted signed JWTs that are to be validated. */
+ @NonnullBeforeExec
+ private List<SignedJWT> signedJwts;
+
+ /**
+ * Set the strategy used to look up a {@link SignedJWT signed JWT token}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setJwtTokenLookupStrategy(@Nonnull final Function<MessageContext, List<SignedJWT>> strategy) {
+ ifInitializedThrowUnmodifiabledComponentException();
+
+ jwtTokenLookupStrategy = Constraint.isNotNull(strategy, "JwtToken lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (jwtTokenLookupStrategy == null) {
+ throw new ComponentInitializationException("JwtTokenLookupStrategy cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+ if (!super.doPreInvoke(messageContext)) {
+ return false;
+ }
+
+ signedJwts = jwtTokenLookupStrategy.apply(messageContext);
+ if (signedJwts == null) {
+ log.debug("{} Extracted JWT was not a SignedJWT, cannot process signature", getLogPrefix());
+ throw new MessageHandlerException("Signed JWT was missing or unpopulated");
+ }
+
+ try {
+ // Test parse of claims.
+ for (SignedJWT signedJwt : signedJwts) {
+ signedJwt.getJWTClaimsSet();
+ }
+ } catch (final ParseException e) {
+ throw new MessageHandlerException("Signed JWT did not have any claims, signature check failed");
+ }
+
+ return true;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ for (SignedJWT signedJwt : signedJwts) {
+
+ if (signedJwt.getState() != State.SIGNED && signedJwt.getState() != State.VERIFIED) {
+ log.debug("{} The JWS object must be in a signed or verified state, cannot process signature",
+ getLogPrefix());
+ throw new MessageHandlerException("Validation of JWS failed. JWT is not signed.");
+ }
+ if (signedJwt.getState() == State.VERIFIED) {
+ log.debug("{} The JWS object was already verified! validating again", getLogPrefix());
+ }
+
+ final OIDCPeerEntityContext peerContext = getOIDCPeerEntityContext();
+ // Add peer identifier in case it is needed by credential resolvers
+ final String issuerId = peerContext != null ? peerContext.getIdentifier() : null;
+ final SignedJWT localSignedJwt = signedJwt;
+ assert localSignedJwt != null;
+ if (evaluate(localSignedJwt, issuerId, messageContext)) {
+ log.debug("{} Validation of JWS token signature succeeded for peer '{}'", getLogPrefix(), issuerId);
+ } else {
+ log.debug("{} Validation of JWS token signature failed for context issuer '{}'", getLogPrefix(),
+ issuerId);
+ throw new MessageHandlerException("Validation of JWS failed");
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
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 15ecc2f..69667ba 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
@@ -37,7 +37,70 @@
<bean id="ParseProof"
class="org.geant.shibboleth.plugin.openidvci.profile.impl.ParseProof" scope="prototype" />
- <bean id="RelyingPartyCredentialResolver" class="net.shibboleth.profile.relyingparty.RelyingPartyCredentialResolver"
+ <bean id="ProofSecurityParametersContextProfileRequestContextLookup" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <ref bean="ProofSecurityParametersContextMessageContextLookup" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <ref bean="shibboleth.MessageContextLookup.Inbound" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="ProofSecurityParametersContextMessageContextLookup" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.oidc.security.jose.context.SecurityParametersContext) }"
+ c:createContext="true" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(org.geant.shibboleth.plugin.openidvci.messaging.context.CredentialsContext) }"
+ c:createContext="true" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="PopulateProofSignatureValidationParameters"
+ class="net.shibboleth.oidc.profile.impl.PopulateJWTSignatureValidationParameters"
+ scope="prototype"
+ c:strategy-ref="shibboleth.MessageContextLookup.Inbound"
+ p:securityParametersContextLookupStrategy-ref="ProofSecurityParametersContextProfileRequestContextLookup">
+ <property name="configurationLookupStrategy">
+ <bean class="org.geant.shibboleth.plugin.openidvci.config.navigate.ProofSignatureValidationConfigurationLookupFunction" />
+ </property>
+ <property name="signatureValidationParametersResolver">
+ <bean class="net.shibboleth.oidc.security.jose.impl.BasicSignatureValidationParametersResolver" />
+ </property>
+ </bean>
+
+ <bean id="ProofsExist" parent="shibboleth.Conditions.Expression"
+ c:expression="#input.getInboundMessageContext() != null and #input.getInboundMessageContext().getSubcontext(T(org.geant.shibboleth.plugin.openidvci.messaging.context.CredentialsContext)) != null and #input.getInboundMessageContext().getSubcontext(T(org.geant.shibboleth.plugin.openidvci.messaging.context.CredentialsContext)).getProofs() != null" />
+
+ <bean id="ValidateProofSignature" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
+ scope="prototype" c:executionDirection="INBOUND"
+ p:activationCondition-ref="ProofsExist"
+ p:errorEvent="#{T(org.geant.shibboleth.plugin.openidvci.profile.OpenIDVCIEventIds).INVALID_PROOF}">
+ <constructor-arg>
+ <bean class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain">
+ <property name="handlers">
+ <list>
+ <bean class="org.geant.shibboleth.plugin.openidvci.security.JWTMessageSignaturesSecurityHandler"
+ scope="prototype"
+ p:securityParametersContextLookupStrategy-ref="ProofSecurityParametersContextMessageContextLookup">
+ <property name="jwtTokenLookupStrategy">
+ <bean
+ class="net.shibboleth.profile.context.navigate.SpringExpressionContextLookupFunction"
+ c:_0="#{ T(org.opensaml.messaging.context.MessageContext) }"
+ c:outputType="#{T(java.util.List)}"
+ c:expression="#input.getParent().ensureInboundMessageContext().ensureSubcontext(T(org.geant.shibboleth.plugin.openidvci.messaging.context.CredentialsContext)).getProofs()" />
+ </property>
+ </bean>
+ </list>
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="RelyingPartyCredentialResolver" class="net.shibboleth.profile.relyingparty.RelyingPartyCredentialResolver"
c:_0-ref="shibboleth.RelyingPartyResolverService" />
<bean id="ValidateExpectedGrantType" class="org.geant.shibboleth.plugin.openidvci.profile.impl.ValidateExpectedGrantType"
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-flow.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-flow.xml
index 8528777..2198133 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-flow.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/credentials/credentials-flow.xml
@@ -29,6 +29,8 @@
</action-state>
<action-state id="ResumeAfterDoDPoPProofValidation">
+ <evaluate expression="PopulateProofSignatureValidationParameters" />
+ <evaluate expression="ValidateProofSignature" />
<evaluate expression="PopulateCredentialsSignatureSigningParameters" />
<evaluate expression="'proceed'"/>
<transition on="proceed" to="BuildResponse"/>
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 db075b8..3979946 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
@@ -30,7 +30,8 @@
p:claimsValidator="#{getObject('DefaultJWTClaimsValidator')}"
p:dpopProofClaimsValidator="#{getObject('DefaultDPoPProofClaimsValidator')}"
p:dpopProofSignatureValidationConfiguration="#{getObject('DPoPSignatureValidationConfiguration')}"
- p:dpopProofNonceGenerator="#{getObject('DefaultOAuth2DPoPNonceGenerator')}" />
+ p:dpopProofNonceGenerator="#{getObject('DefaultOAuth2DPoPNonceGenerator')}"
+ p:proofSignatureValidationConfiguration="#{getObject('ProofSignatureValidationConfiguration')}" />
<bean id="OpenID.VCI.CredentialOffer" parent="AbstractVCIProfile" lazy-init="true"
@@ -73,5 +74,13 @@
p:relyingPartyIdLookupStrategy-ref="openidvci.RelyingPartyForNonce">
</bean>
</util:list>
+
+ <bean id="ProofSignatureValidationConfiguration"
+ parent="shibboleth.oidc.BasicSignatureValidationConfiguration"
+ p:signatureTrustEngine-ref="TokenAsymmetricKeyTrustEngineForProofJWT"/>
+
+ <bean id="TokenAsymmetricKeyTrustEngineForProofJWT"
+ class="net.shibboleth.oidc.security.impl.TokenAsymmetricKeyTrustEngine"
+ c:JOSEObjectResolver-ref="defaultSignedJWTJOSEHeaderCredentialResolver" />
</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list