[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