[java-opensaml] branch main updated: OSJ-362: Add support for dynamic config to Decrypter

Brent Putman putmanb at georgetown.edu
Mon Jul 31 23:17:59 UTC 2023


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

putmanb pushed a commit to branch main
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=f8212e464d29e9a9c3c16478318361464f9bd71c

The following commit(s) were added to refs/heads/main by this push:
     new f8212e464 OSJ-362: Add support for dynamic config to Decrypter
f8212e464 is described below

commit f8212e464d29e9a9c3c16478318361464f9bd71c
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Wed May 31 20:59:34 2023 -0400

    OSJ-362: Add support for dynamic config to Decrypter
---
 .../action/impl/PopulateDecryptionParameters.java  |  31 +++++-
 .../EncryptedElementTypeEncryptedKeyResolver.java  |  19 ++--
 .../saml2/profile/impl/AbstractDecryptAction.java  |   3 +-
 ...cryptedElementTypeEncryptedKeyResolverTest.java |  77 +++++++++-----
 .../opensaml/xmlsec/DecryptionConfiguration.java   |   9 ++
 .../org/opensaml/xmlsec/DecryptionParameters.java  |  23 ++++
 .../criterion/DecryptionRecipientsCriterion.java   | 116 +++++++++++++++++++++
 .../support/AbstractEncryptedKeyResolver.java      |  33 ++++--
 .../support/ChainingEncryptedKeyResolver.java      |  28 +++--
 .../xmlsec/encryption/support/Decrypter.java       |  45 +++++++-
 .../encryption/support/EncryptedKeyResolver.java   |  18 +++-
 .../support/InlineEncryptedKeyResolver.java        |  21 ++--
 ...SimpleKeyInfoReferenceEncryptedKeyResolver.java |  21 +++-
 .../SimpleRetrievalMethodEncryptedKeyResolver.java |  11 +-
 .../xmlsec/impl/BasicDecryptionConfiguration.java  |  23 ++++
 .../impl/BasicDecryptionParametersResolver.java    |  34 ++++++
 .../tests/ChainingEncryptedKeyResolverTest.java    |  70 ++++++++++---
 .../tests/InlineEncryptedKeyResolverTest.java      |  69 ++++++++----
 ...leKeyInfoReferenceEncryptedKeyResolverTest.java |  59 ++++++++---
 ...pleRetrievalMethodEncryptedKeyResolverTest.java |  74 +++++++++----
 .../BasicDecryptionParametersResolverTest.java     |  53 ++++++++++
 21 files changed, 685 insertions(+), 152 deletions(-)

diff --git a/opensaml-profile-impl/src/main/java/org/opensaml/profile/action/impl/PopulateDecryptionParameters.java b/opensaml-profile-impl/src/main/java/org/opensaml/profile/action/impl/PopulateDecryptionParameters.java
index 02f9ddd0c..adcf2b725 100644
--- a/opensaml-profile-impl/src/main/java/org/opensaml/profile/action/impl/PopulateDecryptionParameters.java
+++ b/opensaml-profile-impl/src/main/java/org/opensaml/profile/action/impl/PopulateDecryptionParameters.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.action.AbstractConditionalProfileAction;
@@ -34,6 +35,7 @@ import org.opensaml.xmlsec.DecryptionParametersResolver;
 import org.opensaml.xmlsec.SecurityConfigurationSupport;
 import org.opensaml.xmlsec.context.SecurityParametersContext;
 import org.opensaml.xmlsec.criterion.DecryptionConfigurationCriterion;
+import org.opensaml.xmlsec.criterion.DecryptionRecipientsCriterion;
 import org.slf4j.Logger;
 
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
@@ -65,6 +67,9 @@ public class PopulateDecryptionParameters extends AbstractConditionalProfileActi
     
     /** Resolver for parameters to store into context. */
     @NonnullAfterInit private DecryptionParametersResolver resolver;
+
+    /** Strategy used to look up the {@link SecurityParametersContext} to set the parameters for. */
+    @Nullable private Function<ProfileRequestContext,String> recipientLookupStrategy;
     
     /**
      * Constructor.
@@ -102,6 +107,18 @@ public class PopulateDecryptionParameters extends AbstractConditionalProfileActi
                 "DecryptionConfiguration lookup strategy cannot be null");
     }
     
+    /**
+     * Set the strategy used to look up the dynamic recipient to include in decryption criteria.
+     * 
+     * @param strategy to use
+     */
+    public void setRecipientLookupStrategy(
+            @Nullable final Function<ProfileRequestContext,String> strategy) {
+        checkSetterPreconditions();
+        
+        recipientLookupStrategy = strategy;
+    }
+    
     /**
      * Set the resolver to use for the parameters to store into the context.
      * 
@@ -151,10 +168,18 @@ public class PopulateDecryptionParameters extends AbstractConditionalProfileActi
             return;
         }
         
+        String recipient = null;
+        if (recipientLookupStrategy != null) {
+            recipient = recipientLookupStrategy.apply(profileRequestContext);
+            log.debug("{} Resolved dynamic recipient for decryption: {}", getLogPrefix(), recipient);
+        }
+        
         try {
-            
-            final DecryptionParameters params = resolver.resolveSingle(
-                    new CriteriaSet(new DecryptionConfigurationCriterion(configs)));
+            final CriteriaSet criteria = new CriteriaSet(new DecryptionConfigurationCriterion(configs));
+            if (recipient != null) {
+                criteria.add(new DecryptionRecipientsCriterion(CollectionSupport.singleton(recipient)));
+            }
+            final DecryptionParameters params = resolver.resolveSingle(criteria);
             paramsCtx.setDecryptionParameters(params);
             log.debug("{} {} DecryptionParameters", getLogPrefix(),
                     params != null ? "Resolved" : "Failed to resolve");
diff --git a/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/encryption/EncryptedElementTypeEncryptedKeyResolver.java b/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/encryption/EncryptedElementTypeEncryptedKeyResolver.java
index 6b96e59a5..1a1a3db70 100644
--- a/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/encryption/EncryptedElementTypeEncryptedKeyResolver.java
+++ b/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/encryption/EncryptedElementTypeEncryptedKeyResolver.java
@@ -20,6 +20,7 @@ package org.opensaml.saml.saml2.encryption;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -48,6 +49,7 @@ public class EncryptedElementTypeEncryptedKeyResolver extends AbstractEncryptedK
      * 
      * @param recipients the set of recipients
      */
+    @Deprecated
     public EncryptedElementTypeEncryptedKeyResolver(@Nullable final Set<String> recipients) {
         super(recipients);
     }
@@ -57,27 +59,24 @@ public class EncryptedElementTypeEncryptedKeyResolver extends AbstractEncryptedK
      * 
      * @param recipient the recipient
      */
+    @Deprecated
     public EncryptedElementTypeEncryptedKeyResolver(@Nullable final String recipient) {
         this(recipient != null ? CollectionSupport.singleton(recipient) : null);
     }
 
     /** {@inheritDoc} */
-    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData) {
-        final List<EncryptedKey> resolvedEncKeys = new ArrayList<>();
+    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData,
+            @Nullable final Set<String> recipients) {
         
         if (!(encryptedData.getParent() instanceof EncryptedElementType) ) {
-            return resolvedEncKeys;
+            return CollectionSupport.emptyList();
         }
         
         final EncryptedElementType encElementType = (EncryptedElementType) encryptedData.getParent();
         assert encElementType != null;
-        for (final EncryptedKey encKey : encElementType.getEncryptedKeys()) {
-            if (matchRecipient(encKey.getRecipient())) {
-                resolvedEncKeys.add(encKey);
-            }
-        }
-        
-        return resolvedEncKeys;
+        return  encElementType.getEncryptedKeys().stream()
+                .filter(ek -> matchRecipient(ek.getRecipient(), getEffectiveRecipients(recipients)))
+                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
     
 }
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/AbstractDecryptAction.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/AbstractDecryptAction.java
index 8bad22de7..d688cfeaa 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/AbstractDecryptAction.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/AbstractDecryptAction.java
@@ -197,8 +197,7 @@ public abstract class AbstractDecryptAction extends AbstractProfileAction {
         if (params == null) {
             log.debug("{} No security parameter context or decryption parameters", getLogPrefix());
         } else {
-            decrypter = new Decrypter(params.getDataKeyInfoCredentialResolver(),
-                    params.getKEKKeyInfoCredentialResolver(), params.getEncryptedKeyResolver());
+            decrypter = new Decrypter(params);
         }
         
         return true;
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/encryption/tests/EncryptedElementTypeEncryptedKeyResolverTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/encryption/tests/EncryptedElementTypeEncryptedKeyResolverTest.java
index 133207027..5fae95963 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/encryption/tests/EncryptedElementTypeEncryptedKeyResolverTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/encryption/tests/EncryptedElementTypeEncryptedKeyResolverTest.java
@@ -17,17 +17,13 @@
 
 package org.opensaml.saml.saml2.encryption.tests;
 
-import org.testng.annotations.Test;
-
-import net.shibboleth.shared.collection.CollectionSupport;
-
-import org.testng.Assert;
-
-import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.core.testing.XMLObjectBaseTestCase;
 import org.opensaml.saml.saml2.core.EncryptedAssertion;
@@ -35,6 +31,10 @@ import org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolv
 import org.opensaml.xmlsec.encryption.EncryptedData;
 import org.opensaml.xmlsec.encryption.EncryptedKey;
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.collection.CollectionSupport;
 
 /**
  * Test the SAML EncryptedElementType encrypted key resolver, with keys as peers.
@@ -61,7 +61,7 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         
         resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, null);
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -82,9 +82,9 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         List<EncryptedKey> allKeys = encAssertion.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new EncryptedElementTypeEncryptedKeyResolver(new HashSet<>(CollectionSupport.listOf("foo", "bar", "baz")));
+        resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.setOf("foo", "bar", "baz"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -105,9 +105,9 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         List<EncryptedKey> allKeys = encAssertion.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new EncryptedElementTypeEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -128,9 +128,9 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         List<EncryptedKey> allKeys = encAssertion.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new EncryptedElementTypeEncryptedKeyResolver(CollectionSupport.singleton("bar"));
+        resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("bar"));
         Assert.assertEquals(resolved.size(), 0, "Incorrect number of resolved EncryptedKeys found");
     }
     
@@ -150,7 +150,7 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         
         resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, null);
         Assert.assertEquals(resolved.size(), 4, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -174,9 +174,9 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         List<EncryptedKey> allKeys = encAssertion.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new EncryptedElementTypeEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -197,9 +197,34 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
         List<EncryptedKey> allKeys = encAssertion.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new EncryptedElementTypeEncryptedKeyResolver(new HashSet<>(CollectionSupport.listOf("foo", "baz")));
+        resolver = new EncryptedElementTypeEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.setOf("foo", "baz"));
+        Assert.assertEquals(resolved.size(), 3, "Incorrect number of resolved EncryptedKeys found");
+        
+        Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(1) == allKeys.get(2), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(2) == allKeys.get(3), "Unexpected EncryptedKey instance found");
+    }
+    
+    /** Multi recipient specified to resolver via ctor and method args. */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void  testMultiRecipientsCtorAndArgs() {
+        String filename = "/org/opensaml/saml/saml2/encryption/EncryptedElementTypeEncryptedKeyResolverMultiple.xml";
+        final EncryptedAssertion encAssertion = (EncryptedAssertion) unmarshallElement(filename);
+        assert encAssertion != null;
+        
+        Assert.assertNotNull(encAssertion.getEncryptedData());
+        final EncryptedData encData = encAssertion.getEncryptedData();
+        assert encData != null;
+        
+        List<EncryptedKey> allKeys = encAssertion.getEncryptedKeys();
+        Assert.assertFalse(allKeys.isEmpty());
+        
+        resolver = new EncryptedElementTypeEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("baz"));
         Assert.assertEquals(resolved.size(), 3, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -212,14 +237,14 @@ public class EncryptedElementTypeEncryptedKeyResolverTest extends XMLObjectBaseT
      * 
      * @param encData the EncryptedData context
      * @param ekResolver the resolver to test
+     * @param recipients the valid recipients for resolution
      * @return list of resolved EncryptedKeys
      */
-    @Nonnull private List<EncryptedKey> generateList(@Nonnull final EncryptedData encData, @Nonnull final EncryptedKeyResolver ekResolver) {
-        List<EncryptedKey> resolved = new ArrayList<>();
-        for (EncryptedKey encKey : ekResolver.resolve(encData)) {
-            resolved.add(encKey);
-        }
-        return resolved;
+    @Nonnull private List<EncryptedKey> generateList(@Nonnull final EncryptedData encData,
+            @Nonnull final EncryptedKeyResolver ekResolver, @Nullable final Set<String> recipients) {
+        
+        return StreamSupport.stream(ekResolver.resolve(encData, recipients).spliterator(), false)
+                .collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
     }
 
 }
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionConfiguration.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionConfiguration.java
index fdbf48cea..d50b450d0 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionConfiguration.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionConfiguration.java
@@ -17,6 +17,8 @@
 
 package org.opensaml.xmlsec;
 
+import java.util.Set;
+
 import javax.annotation.Nullable;
 
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
@@ -49,4 +51,11 @@ public interface DecryptionConfiguration extends AlgorithmPolicyConfiguration {
      */
     @Nullable EncryptedKeyResolver getEncryptedKeyResolver();
     
+    /**
+     * Get the set of recipients against which to evaluate candidate EncryptedKey elements.
+     * 
+     * @return the recipients
+     */
+    @Nullable Set<String> getRecipients();
+    
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionParameters.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionParameters.java
index 9f759ed8e..13680eeb1 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionParameters.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/DecryptionParameters.java
@@ -17,6 +17,8 @@
 
 package org.opensaml.xmlsec;
 
+import java.util.Set;
+
 import javax.annotation.Nullable;
 
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
@@ -35,6 +37,9 @@ public class DecryptionParameters extends AlgorithmPolicyParameters {
     
     /** The EncryptedKey resolver. */
     @Nullable private EncryptedKeyResolver encryptedKeyResolver;
+    
+    /** The set of recipients against which to evaluate candidate EncryptedKey elements. */
+    @Nullable private Set<String> recipients;
         
     /**
      * Get the KeyInfoCredentialResolver to use when processing the EncryptedData/KeyInfo.
@@ -91,5 +96,23 @@ public class DecryptionParameters extends AlgorithmPolicyParameters {
     public void setEncryptedKeyResolver(@Nullable final EncryptedKeyResolver resolver) {
        encryptedKeyResolver = resolver; 
     }
+
+    /**
+     * Get the set of recipients against which to evaluate candidate EncryptedKey elements.
+     * 
+     * @return the recipients
+     */
+    public @Nullable Set<String> getRecipients() {
+        return recipients;
+    }
+
+    /**
+     * Set the set of recipients against which to evaluate candidate EncryptedKey elements.
+     * 
+     * @param newRecipients the recipients
+     */
+    public void setRecipients(@Nullable Set<String> newRecipients) {
+        recipients = newRecipients;
+    }
     
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/criterion/DecryptionRecipientsCriterion.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/criterion/DecryptionRecipientsCriterion.java
new file mode 100644
index 000000000..5b9ac76bc
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/criterion/DecryptionRecipientsCriterion.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.opensaml.xmlsec.criterion;
+
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.resolver.Criterion;
+
+/**
+ * A criterion implementation for conveying set of recipients against which to evaluate candidate
+ * EncryptedKey elements.
+ */
+public class DecryptionRecipientsCriterion implements Criterion {
+    
+    /** The set of recipients. */
+    @Nonnull private Set<String> recipients;
+    
+    /**
+     * Constructor.
+     *
+     * @param values the set of recipients
+     */
+    public DecryptionRecipientsCriterion(@Nullable final Set<String> values)  {
+        recipients = processValues(values);
+    }
+    
+    /**
+     * Get the set of recipients
+     * 
+     * @return the set of recipients
+     */
+    @Nonnull @NotLive @Unmodifiable public Set<String> getRecipients() {
+        return recipients;
+    }
+    
+    /**
+     * Set the set of recipients
+     * 
+     * @param values the new recipients
+     */
+    public void setRecipients(@Nullable final Set<String> values) {
+        recipients = processValues(values);
+    }
+    
+    /** {@inheritDoc} */
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("DecryptionRecipientsCriterion [values=");
+        builder.append(recipients);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        int result = 17;  
+        result = 37*result + recipients.hashCode();
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof DecryptionRecipientsCriterion) {
+            final DecryptionRecipientsCriterion other = (DecryptionRecipientsCriterion) obj;
+            return recipients.equals(other.recipients);
+        }
+
+        return false;
+    }
+    
+    /**
+     * Sanitize input values.
+     * 
+     * @param values input values
+     * 
+     * @return sanitized set
+     */
+    @Nonnull @NotLive @Unmodifiable private Set<String> processValues(@Nullable final Set<String> values) {
+        if (values!= null) {
+            return CollectionSupport.copyToSet(StringSupport.normalizeStringCollection(values));
+        } else {
+            return CollectionSupport.emptySet();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/AbstractEncryptedKeyResolver.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/AbstractEncryptedKeyResolver.java
index c65cbc640..650a0876f 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/AbstractEncryptedKeyResolver.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/AbstractEncryptedKeyResolver.java
@@ -17,6 +17,7 @@
 
 package org.opensaml.xmlsec.encryption.support;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -57,6 +58,7 @@ public abstract class AbstractEncryptedKeyResolver implements EncryptedKeyResolv
      * 
      * @param newRecipents set of recipients
      */
+    @Deprecated
     public AbstractEncryptedKeyResolver(@Nullable final Set<String> newRecipents) {
         recipients = CollectionSupport.copyToSet(StringSupport.normalizeStringCollection(newRecipents));
     }
@@ -76,29 +78,48 @@ public abstract class AbstractEncryptedKeyResolver implements EncryptedKeyResolv
     }
 
     /** {@inheritDoc} */
+    @Deprecated
     @Nonnull @Unmodifiable @NotLive public Set<String> getRecipients() {
         return recipients;
     }
     
+    /**
+     * Get the effective set of recipients by merging the passed recipients set
+     * with the static set of recipients possibly configured on this resolver instance. 
+     * 
+     * @param values the recipients argument
+     * @return the merged recipients
+     */
+    @Nonnull @Unmodifiable @NotLive
+    protected Set<String> getEffectiveRecipients(@Nullable Set<String> values) {
+        final Set<String> temp = new HashSet<>();
+        temp.addAll(getRecipients());
+        if (values != null) {
+            temp.addAll(values);
+        }
+        return CollectionSupport.copyToSet(temp);
+    }
+    
     /**
      * Evaluate whether the specified recipient attribute value matches this resolver's
      * recipient criteria.
      * 
      * @param recipient the recipient value to evaluate
+     * @param validRecipients recipients to consider valid for matching purposes.
+     *        If empty, then all recipients match
      * @return true if the recipient value matches the resolver's criteria, false otherwise
      */
-    protected boolean matchRecipient(@Nullable final String recipient) {
-        final String trimmedRecipient = StringSupport.trimOrNull(recipient);
-        if (trimmedRecipient == null) {
+    protected boolean matchRecipient(@Nullable final String recipient, @Nonnull Set<String> validRecipients) {
+        if (validRecipients.isEmpty()) {
             return true;
         }
         
-        final Set<String> ourRecipients = getRecipients();
-        if (ourRecipients.isEmpty()) {
+        final String trimmedRecipient = StringSupport.trimOrNull(recipient);
+        if (trimmedRecipient == null) {
             return true;
         }
         
-        return ourRecipients.contains(trimmedRecipient);
+        return validRecipients.contains(trimmedRecipient);
     }
     
     /**
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/ChainingEncryptedKeyResolver.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/ChainingEncryptedKeyResolver.java
index 6bf3679ba..437f00449 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/ChainingEncryptedKeyResolver.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/ChainingEncryptedKeyResolver.java
@@ -65,6 +65,7 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
      * @param encKeyResolvers the chain of encrypted key resolvers
      * @param recipients the set of recipients
      */
+    @Deprecated
     public ChainingEncryptedKeyResolver(
             @Nonnull @ParameterName(name="encKeyResolvers") final List<EncryptedKeyResolver> encKeyResolvers,
             @Nullable @ParameterName(name="recipients") final Set<String> recipients) {
@@ -79,6 +80,7 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
      * @param encKeyResolvers the chain of encrypted key resolvers
      * @param recipient the recipient
      */
+    @Deprecated
     public ChainingEncryptedKeyResolver(
             @Nonnull @ParameterName(name="encKeyResolvers") final List<EncryptedKeyResolver> encKeyResolvers,
             @Nullable @ParameterName(name="recipient") final String recipient) {
@@ -96,12 +98,13 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
 
     /** {@inheritDoc} */
     @Override
-    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData) {
+    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData,
+            @Nullable final Set<String> recipients) {
         if (resolvers.isEmpty()) {
             log.warn("Chaining encrypted key resolver resolution was attempted with an empty resolver chain");
             throw new IllegalStateException("The resolver chain is empty");
         }
-        return new ChainingIterable(this, encryptedData);
+        return new ChainingIterable(this, encryptedData, recipients);
     }
 
     /**
@@ -114,22 +117,28 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
 
         /** The EncryptedData context for resolution. */
         @Nonnull private final EncryptedData encryptedData;
+        
+        /** The recipients for resolution. */
+        @Nullable private final Set<String> recipients;
 
         /**
          * Constructor.
          * 
          * @param resolver the ChainingEncryptedKeyResolver parent
          * @param encData the EncryptedData context for resolution
+         * @param recipientIDs the recipients for resolution
          */
         public ChainingIterable(@Nonnull final ChainingEncryptedKeyResolver resolver,
-                @Nonnull final EncryptedData encData) {
+                @Nonnull final EncryptedData encData, @Nullable final Set<String> recipientIDs) {
             parent = resolver;
             encryptedData = encData;
+            recipients = recipientIDs;
+            
         }
 
         /** {@inheritDoc} */
         @Nonnull public Iterator<EncryptedKey> iterator() {
-            return new ChainingIterator(parent, encryptedData);
+            return new ChainingIterator(parent, encryptedData, recipients);
         }
 
     }
@@ -150,6 +159,9 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
         /** The EncryptedData context for resolution. */
         @Nonnull private final EncryptedData encryptedData;
 
+        /** The recipients for resolution. */
+        @Nonnull @Unmodifiable @NotLive private final Set<String> validRecipients;
+
         /** The iterator over resolvers in the chain. */
         @Nonnull private final Iterator<EncryptedKeyResolver> resolverIterator;
 
@@ -167,11 +179,13 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
          * 
          * @param resolver the ChainingEncryptedKeyResolver parent
          * @param encData the EncryptedData context for resolution
+         * @param recipientIDs the recipients for resolution
          */
         public ChainingIterator(@Nonnull final ChainingEncryptedKeyResolver resolver,
-                @Nonnull final EncryptedData encData) {
+                @Nonnull final EncryptedData encData, @Nullable final Set<String> recipientIDs) {
             parent = resolver;
             encryptedData = encData;
+            validRecipients = getEffectiveRecipients(recipientIDs);
             resolverIterator = parent.getResolverChain().iterator();
             keyIterator = getNextKeyIterator();
             nextKey = null;
@@ -239,7 +253,7 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
             if (keyIterator != null) {
                 while (keyIterator.hasNext()) {
                     tempKey = keyIterator.next();
-                    if (parent.matchRecipient(tempKey.getRecipient())) {
+                    if (parent.matchRecipient(tempKey.getRecipient(), validRecipients)) {
                         log.debug("Found matching encrypted key: {}", tempKey.toString());
                         return tempKey;
                     }
@@ -250,7 +264,7 @@ public class ChainingEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
             while (keyIterator != null) {
                 while (keyIterator.hasNext()) {
                     tempKey = keyIterator.next();
-                    if (parent.matchRecipient(tempKey.getRecipient())) {
+                    if (parent.matchRecipient(tempKey.getRecipient(), validRecipients)) {
                         log.debug("Found matching encrypted key: {}", tempKey.toString());
                         return tempKey;
                     }
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java
index a6719006d..3d70d5f48 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java
@@ -205,6 +205,9 @@ public class Decrypter {
     /** Resolver for EncryptedKey instances which contain the encrypted data encryption key. */
     @Nullable private EncryptedKeyResolver encKeyResolver;
     
+    /** The set of recipients against which to evaluate candidate EncryptedKey elements. */
+    @Nullable private Set<String> recipients;
+    
     /** The collection of algorithm URIs which are included. */
     @Nullable private Collection<String> includedAlgorithmURIs;
     
@@ -236,6 +239,7 @@ public class Decrypter {
         this(   params.getDataKeyInfoCredentialResolver(),
                 params.getKEKKeyInfoCredentialResolver(),
                 params.getEncryptedKeyResolver(),
+                params.getRecipients(),
                 params.getIncludedAlgorithms(),
                 params.getExcludedAlgorithms()
                 );
@@ -252,7 +256,23 @@ public class Decrypter {
             @Nullable final KeyInfoCredentialResolver newKEKResolver,
             @Nullable final EncryptedKeyResolver newEncKeyResolver) {
         
-        this(newResolver, newKEKResolver, newEncKeyResolver, null, null);
+        this(newResolver, newKEKResolver, newEncKeyResolver, null, null, null);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param newResolver resolver for data encryption keys.
+     * @param newKEKResolver resolver for key encryption keys.
+     * @param newEncKeyResolver resolver for EncryptedKey elements
+     * @param newRecipients valid recipients of EncryptedKey elements
+     */
+    public Decrypter(@Nullable final KeyInfoCredentialResolver newResolver,
+            @Nullable final KeyInfoCredentialResolver newKEKResolver,
+            @Nullable final EncryptedKeyResolver newEncKeyResolver,
+            @Nullable final Set<String> newRecipients) {
+        
+        this(newResolver, newKEKResolver, newEncKeyResolver, newRecipients, null, null);
     }
     
     /**
@@ -269,12 +289,33 @@ public class Decrypter {
             @Nullable final EncryptedKeyResolver newEncKeyResolver,
             @Nullable final Collection<String> includeAlgos,
             @Nullable final Collection<String> excludeAlgos) {
+
+        this(newResolver, newKEKResolver, newEncKeyResolver, null, includeAlgos, excludeAlgos);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param newResolver resolver for data encryption keys.
+     * @param newKEKResolver resolver for key encryption keys.
+     * @param newEncKeyResolver resolver for EncryptedKey elements
+     * @param newRecipients valid recipients of EncryptedKey elements
+     * @param includeAlgos collection of included algorithm URIs
+     * @param excludeAlgos collection of excluded algorithm URIs
+     */
+    public Decrypter(@Nullable final KeyInfoCredentialResolver newResolver,
+            @Nullable final KeyInfoCredentialResolver newKEKResolver,
+            @Nullable final EncryptedKeyResolver newEncKeyResolver,
+            @Nullable final Set<String> newRecipients,
+            @Nullable final Collection<String> includeAlgos,
+            @Nullable final Collection<String> excludeAlgos) {
         
         this();
         
         resolver = newResolver;
         kekResolver = newKEKResolver;
         encKeyResolver = newEncKeyResolver;
+        recipients = newRecipients;
         includedAlgorithmURIs = includeAlgos;
         excludedAlgorithmURIs = excludeAlgos;
     }
@@ -838,7 +879,7 @@ public class Decrypter {
     @Nullable private DocumentFragment decryptUsingResolvedEncryptedKey(@Nonnull final EncryptedData encryptedData,
             @Nonnull final String algorithm) {
         if (encKeyResolver != null) {
-            for (final EncryptedKey encryptedKey : encKeyResolver.resolve(encryptedData)) {
+            for (final EncryptedKey encryptedKey : encKeyResolver.resolve(encryptedData, recipients)) {
                 try {
                     assert encryptedKey != null;
                     final Key decryptedKey = decryptKey(encryptedKey, algorithm);
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/EncryptedKeyResolver.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/EncryptedKeyResolver.java
index f94b757e2..f9358dac5 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/EncryptedKeyResolver.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/EncryptedKeyResolver.java
@@ -20,12 +20,14 @@ package org.opensaml.xmlsec.encryption.support;
 import java.util.Set;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.xmlsec.encryption.EncryptedData;
 import org.opensaml.xmlsec.encryption.EncryptedKey;
 
 import net.shibboleth.shared.annotation.constraint.NotLive;
 import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
 
 /**
  * Interface for resolving {@link EncryptedKey} elements based on a particular
@@ -43,14 +45,28 @@ public interface EncryptedKeyResolver {
      * @param encryptedData  the EncryptedData element context in which to resolve
      * @return an iterable of EncryptedKey elements
      */
-    @Nonnull Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData);
+    default @Nonnull Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData) {
+        return resolve(encryptedData, CollectionSupport.emptySet());
+    }
     
+    /**
+     * Resolve the EncryptedKey elements containing the data encryption key used to 
+     * encrypt the specified EncryptedData element.
+     * 
+     * @param encryptedData  the EncryptedData element context in which to resolve
+     * @param recipients the recipients to use during resolution
+     * @return an iterable of EncryptedKey elements
+     */
+    @Nonnull Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData,
+            @Nullable final Set<String> recipients);
+
     /**
      * Get the set of recipient criteria used by this resolver, and against which a candidate 
      * EncryptedKey's Recipient attribute is evaluated.
      * 
      * @return the collection of  recipient criteria
      */
+    @Deprecated
     @Nonnull @Unmodifiable @NotLive Set<String> getRecipients();
 
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/InlineEncryptedKeyResolver.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/InlineEncryptedKeyResolver.java
index c54b0b7a5..0eacbe0bf 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/InlineEncryptedKeyResolver.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/InlineEncryptedKeyResolver.java
@@ -1,5 +1,6 @@
 /*
  * Licensed to the University Corporation for Advanced Internet Development,
+
  * Inc. (UCAID) under one or more contributor license agreements.  See the
  * NOTICE file distributed with this work for additional information regarding
  * copyright ownership. The UCAID licenses this file to You under the Apache
@@ -20,6 +21,7 @@ package org.opensaml.xmlsec.encryption.support;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -47,6 +49,7 @@ public class InlineEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
      * 
      * @param recipients the set of recipients
      */
+    @Deprecated
     public InlineEncryptedKeyResolver(@Nullable final Set<String> recipients) {
         super(recipients);
     }
@@ -56,28 +59,24 @@ public class InlineEncryptedKeyResolver extends AbstractEncryptedKeyResolver {
      * 
      * @param recipient the recipient
      */
+    @Deprecated
     public InlineEncryptedKeyResolver(@Nullable final String recipient) {
         this(recipient != null ? CollectionSupport.singleton(recipient) : null);
     }
 
     /** {@inheritDoc} */
-    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData) {
+    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData,
+            @Nullable final Set<String> recipients) {
         Constraint.isNotNull(encryptedData, "EncryptedData cannot be null");
         
-        final List<EncryptedKey> resolvedEncKeys = new ArrayList<>();
-        
         final KeyInfo keyInfo = encryptedData.getKeyInfo();
         if (keyInfo == null) {
-            return resolvedEncKeys;
-        }
-        
-        for (final EncryptedKey encKey : keyInfo.getEncryptedKeys()) {
-            if (matchRecipient(encKey.getRecipient())) {
-                resolvedEncKeys.add(encKey);
-            }
+            return CollectionSupport.emptyList();
         }
         
-        return resolvedEncKeys;
+        return keyInfo.getEncryptedKeys().stream()
+                .filter(ek -> matchRecipient(ek.getRecipient(), getEffectiveRecipients(recipients)))
+                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleKeyInfoReferenceEncryptedKeyResolver.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleKeyInfoReferenceEncryptedKeyResolver.java
index 190ae0ac6..7ab6f6375 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleKeyInfoReferenceEncryptedKeyResolver.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleKeyInfoReferenceEncryptedKeyResolver.java
@@ -31,6 +31,9 @@ import org.opensaml.xmlsec.signature.KeyInfo;
 import org.opensaml.xmlsec.signature.KeyInfoReference;
 import org.slf4j.Logger;
 
+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;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -63,6 +66,7 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolver extends AbstractEncrypte
      * 
      * @param recipients the set of recipients
      */
+    @Deprecated
     public SimpleKeyInfoReferenceEncryptedKeyResolver(@Nullable final Set<String> recipients) {
         super(recipients);
         depthLimit = 5;
@@ -73,6 +77,7 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolver extends AbstractEncrypte
      * 
      * @param recipient the recipient
      */
+    @Deprecated
     public SimpleKeyInfoReferenceEncryptedKeyResolver(@Nullable final String recipient) {
         this(recipient != null ? CollectionSupport.singleton(recipient) : null);
     }
@@ -97,10 +102,13 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolver extends AbstractEncrypte
     
     /** {@inheritDoc} */
     @Override
-    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData) {
+    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData,
+            @Nullable final Set<String> recipients) {
         Constraint.isNotNull(encryptedData, "EncryptedData cannot be null");
         
-        return resolveKeyInfo(encryptedData.getKeyInfo(), depthLimit);
+        final Set<String> validRecipients = getEffectiveRecipients(recipients);
+        
+        return resolveKeyInfo(encryptedData.getKeyInfo(), depthLimit, validRecipients);
     }
 
     /**
@@ -108,9 +116,12 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolver extends AbstractEncrypte
      * 
      * @param keyInfo KeyInfo to process
      * @param limit depth of references to follow
+     * @param validRecipients recipients to consider valid for matching purposes.
      * @return  encrypted keys
      */
-    @Nonnull protected Iterable<EncryptedKey> resolveKeyInfo(@Nullable final KeyInfo keyInfo, final int limit) {
+    @Nonnull protected Iterable<EncryptedKey> resolveKeyInfo(@Nullable final KeyInfo keyInfo, final int limit,
+            @Nonnull @NonnullElements @Unmodifiable @NotLive final Set<String> validRecipients) {
+
         final List<EncryptedKey> resolvedEncKeys = new ArrayList<>();
         
         if (keyInfo == null) {
@@ -121,7 +132,7 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolver extends AbstractEncrypte
         // After that, we always start by looking inline.
         if (limit < depthLimit) {
             for (final EncryptedKey encKey : keyInfo.getEncryptedKeys()) {
-                if (matchRecipient(encKey.getRecipient())) {
+                if (matchRecipient(encKey.getRecipient(), validRecipients)) {
                     resolvedEncKeys.add(encKey);
                 }
             }
@@ -132,7 +143,7 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolver extends AbstractEncrypte
         } else {
             for (final KeyInfoReference ref : keyInfo.getKeyInfoReferences()) {
                 assert ref != null;
-                for (final EncryptedKey encKey : resolveKeyInfo(dereferenceURI(ref), limit-1)) {
+                for (final EncryptedKey encKey : resolveKeyInfo(dereferenceURI(ref), limit-1, validRecipients)) {
                     resolvedEncKeys.add(encKey);
                 }
             }
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleRetrievalMethodEncryptedKeyResolver.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleRetrievalMethodEncryptedKeyResolver.java
index 48b989719..d9de96e65 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleRetrievalMethodEncryptedKeyResolver.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/SimpleRetrievalMethodEncryptedKeyResolver.java
@@ -61,6 +61,7 @@ public class SimpleRetrievalMethodEncryptedKeyResolver extends AbstractEncrypted
      * 
      * @param recipients the set of recipients
      */
+    @Deprecated
     public SimpleRetrievalMethodEncryptedKeyResolver(@Nullable final Set<String> recipients) {
         super(recipients);
     }
@@ -70,19 +71,21 @@ public class SimpleRetrievalMethodEncryptedKeyResolver extends AbstractEncrypted
      * 
      * @param recipient the recipient
      */
+    @Deprecated
     public SimpleRetrievalMethodEncryptedKeyResolver(@Nullable final String recipient) {
         this(recipient != null ? CollectionSupport.singleton(recipient) : null);
     }
 
     /** {@inheritDoc} */
-    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData) {
+    @Nonnull public Iterable<EncryptedKey> resolve(@Nonnull final EncryptedData encryptedData,
+            @Nullable final Set<String> recipients) {
         Constraint.isNotNull(encryptedData, "EncryptedData cannot be null");
         
         final List<EncryptedKey> resolvedEncKeys = new ArrayList<>();
 
         final KeyInfo keyInfo = encryptedData.getKeyInfo();
         if (keyInfo == null) {
-            return resolvedEncKeys;
+            return CollectionSupport.emptyList();
         }
 
         for (final RetrievalMethod rm : keyInfo.getRetrievalMethods()) {
@@ -92,11 +95,13 @@ public class SimpleRetrievalMethodEncryptedKeyResolver extends AbstractEncrypted
                 log.warn("EncryptedKey RetrievalMethod has transforms, cannot process");
                 continue;
             }
+            
+            final Set<String> validRecipients = getEffectiveRecipients(recipients);
 
             final EncryptedKey encKey = dereferenceURI(rm);
             if (encKey == null) {
                 continue;
-            } else if (matchRecipient(encKey.getRecipient())) {
+            } else if (matchRecipient(encKey.getRecipient(), validRecipients)) {
                 resolvedEncKeys.add(encKey);
             }
         }
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionConfiguration.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionConfiguration.java
index c7baac153..1841b0165 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionConfiguration.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionConfiguration.java
@@ -17,6 +17,8 @@
 
 package org.opensaml.xmlsec.impl;
 
+import java.util.Set;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -38,6 +40,9 @@ public class BasicDecryptionConfiguration extends BasicAlgorithmPolicyConfigurat
     
     /** The EncryptedKey resolver. */
     @Nullable private EncryptedKeyResolver encryptedKeyResolver;
+    
+    /** The set of recipients against which to evaluate candidate EncryptedKey elements. */
+    @Nullable private Set<String> recipients;
 
     //TODO chaining to parent config instance on getters? or use a wrapping proxy, etc?
 
@@ -109,4 +114,22 @@ public class BasicDecryptionConfiguration extends BasicAlgorithmPolicyConfigurat
        return this;
     }
     
+    /**
+     * Get the set of recipients against which to evaluate candidate EncryptedKey elements.
+     * 
+     * @return the recipients
+     */
+    public @Nullable Set<String> getRecipients() {
+        return recipients;
+    }
+
+    /**
+     * Set the set of recipients against which to evaluate candidate EncryptedKey elements.
+     * 
+     * @param newRecipients the recipients
+     */
+    public void setRecipients(@Nullable Set<String> newRecipients) {
+        recipients = newRecipients;
+    }
+    
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolver.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolver.java
index 8cefc23f2..2f51f4335 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolver.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolver.java
@@ -17,6 +17,9 @@
 
 package org.opensaml.xmlsec.impl;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -30,6 +33,7 @@ import org.opensaml.xmlsec.DecryptionConfiguration;
 import org.opensaml.xmlsec.DecryptionParameters;
 import org.opensaml.xmlsec.DecryptionParametersResolver;
 import org.opensaml.xmlsec.criterion.DecryptionConfigurationCriterion;
+import org.opensaml.xmlsec.criterion.DecryptionRecipientsCriterion;
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
 import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
 import org.slf4j.Logger;
@@ -79,6 +83,7 @@ public class BasicDecryptionParametersResolver extends AbstractSecurityParameter
         params.setDataKeyInfoCredentialResolver(resolveDataKeyInfoCredentialResolver(criteria));
         params.setKEKKeyInfoCredentialResolver(resolveKEKKeyInfoCredentialResolver(criteria));
         params.setEncryptedKeyResolver(resolveEncryptedKeyResolver(criteria));
+        params.setRecipients(resolveRecipients(criteria));
         
         logResult(params);
         
@@ -103,6 +108,8 @@ public class BasicDecryptionParametersResolver extends AbstractSecurityParameter
                     params.getKEKKeyInfoCredentialResolver() != null ? "present" : "null");
             log.debug("\tEncryptedKeyResolver: {}", 
                     params.getEncryptedKeyResolver() != null ? "present" : "null");
+
+            log.debug("\tRecipients: {}", params.getRecipients());
             
         }
     }
@@ -169,4 +176,31 @@ public class BasicDecryptionParametersResolver extends AbstractSecurityParameter
         return null;
     }
 
+    /**
+     * Resolve the effective set of recipients against which to evaluate candidate EncryptedKey elements.
+     * 
+     * @param criteria the input criteria being evaluated
+     * @return the recipients set, or null
+     */
+    private Set<String> resolveRecipients(@Nonnull final CriteriaSet criteria) {
+        final DecryptionConfigurationCriterion configCriterion = criteria.get(DecryptionConfigurationCriterion.class);
+        assert configCriterion != null;
+        
+        final Set<String> recipients = new HashSet<>();
+
+        for (final DecryptionConfiguration config : configCriterion.getConfigurations()) {
+            if (config.getRecipients() != null) {
+                recipients.addAll(config.getRecipients());
+                break;
+            }
+        }
+        
+        final DecryptionRecipientsCriterion recipientsCriterion = criteria.get(DecryptionRecipientsCriterion.class);
+        if (recipientsCriterion != null) {
+            recipients.addAll(recipientsCriterion.getRecipients());
+        }
+
+        return recipients.isEmpty() ? null : recipients;
+    }
+
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ChainingEncryptedKeyResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ChainingEncryptedKeyResolverTest.java
index 98b2c7bd2..8a4ee5ac7 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ChainingEncryptedKeyResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ChainingEncryptedKeyResolverTest.java
@@ -22,8 +22,11 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.core.testing.XMLObjectBaseTestCase;
 import org.opensaml.core.xml.XMLObject;
@@ -39,6 +42,8 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import net.shibboleth.shared.collection.CollectionSupport;
+
 /**
  * Test the encrypted key resolver which dereferences RetrievalMethods.
  */
@@ -84,7 +89,7 @@ public class ChainingEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         
         resolver = new ChainingEncryptedKeyResolver(new ArrayList<EncryptedKeyResolver>());
         
-        generateList(encData, resolver);
+        generateList(encData, resolver, null);
     }
     
     /** One recipient specified to resolver, EncryptedKey in instance inline. */
@@ -108,9 +113,9 @@ public class ChainingEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         
         recipients.add("foo");
         
-        resolver = new ChainingEncryptedKeyResolver(resolverChain, recipients);
+        resolver = new ChainingEncryptedKeyResolver(resolverChain);
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, recipients);
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -137,9 +142,9 @@ public class ChainingEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         
         recipients.add("foo");
         
-        resolver = new ChainingEncryptedKeyResolver(resolverChain, recipients);
+        resolver = new ChainingEncryptedKeyResolver(resolverChain);
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, recipients);
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -166,9 +171,9 @@ public class ChainingEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         
         recipients.add("foo");
         
-        resolver = new ChainingEncryptedKeyResolver(resolverChain, recipients);
+        resolver = new ChainingEncryptedKeyResolver(resolverChain);
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, recipients);
         Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -197,9 +202,42 @@ public class ChainingEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         recipients.add("foo");
         recipients.add("baz");
         
-        resolver = new ChainingEncryptedKeyResolver(resolverChain, recipients);
+        resolver = new ChainingEncryptedKeyResolver(resolverChain);
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, recipients);
+        Assert.assertEquals(resolved.size(), 4, "Incorrect number of resolved EncryptedKeys found");
+        
+        Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(1) == allKeys.get(2), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(2) == allKeys.get(3), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(3) == allKeys.get(5), "Unexpected EncryptedKey instance found");
+    }
+    
+    /** Multi recipient specified to resolver via ctor and method args. */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testMultiRecipientsCtorAndArgs() {
+        String filename =  "/org/opensaml/xmlsec/encryption/support/ChainingEncryptedKeyResolverMultiple.xml";
+        SignableSimpleXMLObject sxo =  (SignableSimpleXMLObject) unmarshallElement(filename);
+        assert sxo != null;
+        Assert.assertNotNull(sxo.getSimpleXMLObjects().get(0));
+        Assert.assertNotNull(sxo.getSimpleXMLObjects().get(0).getEncryptedData());
+        
+        final EncryptedData encData = sxo.getSimpleXMLObjects().get(0).getEncryptedData();
+        assert encData != null;
+        final KeyInfo keyInfo = encData.getKeyInfo();
+        assert keyInfo != null;
+        Assert.assertFalse(keyInfo.getEncryptedKeys().isEmpty());
+        Assert.assertFalse(keyInfo.getRetrievalMethods().isEmpty());
+        
+        List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
+        Assert.assertFalse(allKeys.isEmpty());
+        
+        recipients.add("baz");
+        
+        resolver = new ChainingEncryptedKeyResolver(resolverChain, CollectionSupport.singleton("foo"));
+        
+        List<EncryptedKey> resolved = generateList(encData, resolver, recipients);
         Assert.assertEquals(resolved.size(), 4, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -229,20 +267,20 @@ public class ChainingEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         }
         return allKeys;
     }
-
+    
     /**
      * Resolve EncryptedKeys and put them in an ordered list.
      * 
      * @param encData the EncryptedData context
      * @param ekResolver the resolver to test
+     * @param recipients the valid recipients for resolution
      * @return list of resolved EncryptedKeys
      */
-    private List<EncryptedKey> generateList(EncryptedData encData, EncryptedKeyResolver ekResolver) {
-        List<EncryptedKey> resolved = new ArrayList<>();
-        for (EncryptedKey encKey : ekResolver.resolve(encData)) {
-            resolved.add(encKey);
-        }
-        return resolved;
+    @Nonnull private List<EncryptedKey> generateList(@Nonnull final EncryptedData encData,
+            @Nonnull final EncryptedKeyResolver ekResolver, @Nullable final Set<String> recipients) {
+        
+        return StreamSupport.stream(ekResolver.resolve(encData, recipients).spliterator(), false)
+                .collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
     }
 
 
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/InlineEncryptedKeyResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/InlineEncryptedKeyResolverTest.java
index c101aac6f..900367949 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/InlineEncryptedKeyResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/InlineEncryptedKeyResolverTest.java
@@ -17,16 +17,13 @@
 
 package org.opensaml.xmlsec.encryption.support.tests;
 
-import org.testng.annotations.Test;
-
-import net.shibboleth.shared.collection.CollectionSupport;
-
-import org.testng.Assert;
-
-import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.core.testing.XMLObjectBaseTestCase;
 import org.opensaml.xmlsec.encryption.EncryptedData;
@@ -34,6 +31,10 @@ import org.opensaml.xmlsec.encryption.EncryptedKey;
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
 import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
 import org.opensaml.xmlsec.signature.KeyInfo;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.collection.CollectionSupport;
 
 /**
  * Test the inline encrypted key resolver.
@@ -58,7 +59,7 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         
         resolver = new InlineEncryptedKeyResolver();
         
-        final List<EncryptedKey> resolved = generateList(encData, resolver);
+        final List<EncryptedKey> resolved = generateList(encData, resolver, null);
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -76,9 +77,9 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         final List<EncryptedKey> allKeys = keyInfo.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new InlineEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new InlineEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -96,9 +97,9 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         final List<EncryptedKey> allKeys = keyInfo.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new InlineEncryptedKeyResolver(CollectionSupport.singleton("bar"));
+        resolver = new InlineEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("bar"));
         Assert.assertEquals(resolved.size(), 0, "Incorrect number of resolved EncryptedKeys found");
     }
     
@@ -116,7 +117,7 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         
         resolver = new InlineEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, null);
         Assert.assertEquals(resolved.size(), 4, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -138,9 +139,9 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         final List<EncryptedKey> allKeys = keyInfo.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new InlineEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new InlineEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -159,9 +160,9 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         final List<EncryptedKey> allKeys = keyInfo.getEncryptedKeys();
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new InlineEncryptedKeyResolver(CollectionSupport.setOf("foo", "baz"));
+        resolver = new InlineEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.setOf("foo", "baz"));
         Assert.assertEquals(resolved.size(), 3, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -169,20 +170,42 @@ public class InlineEncryptedKeyResolverTest extends XMLObjectBaseTestCase {
         Assert.assertTrue(resolved.get(2) == allKeys.get(3), "Unexpected EncryptedKey instance found");
     }
     
+    /** Multi recipient specified to resolver via ctor and method args. */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void  testMultiRecipientsCtorAndArgs() {
+        final String filename = "/org/opensaml/xmlsec/encryption/support/InlineEncryptedKeyResolverMultiple.xml";
+        final EncryptedData encData = (EncryptedData) unmarshallElement(filename);
+        assert encData != null;
+        
+        final KeyInfo keyInfo = encData.getKeyInfo();
+        assert keyInfo != null;
+        final List<EncryptedKey> allKeys = keyInfo.getEncryptedKeys();
+        Assert.assertFalse(allKeys.isEmpty());
+        
+        resolver = new InlineEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("baz"));
+        Assert.assertEquals(resolved.size(), 3, "Incorrect number of resolved EncryptedKeys found");
+        
+        Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(1) == allKeys.get(2), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(2) == allKeys.get(3), "Unexpected EncryptedKey instance found");
+    }
+
     /**
      * Resolve EncryptedKeys and put them in an ordered list.
      * 
      * @param encData the EncryptedData context
      * @param ekResolver the resolver to test
+     * @param recipients the valid recipients for resolution
      * @return list of resolved EncryptedKeys
      */
     @Nonnull private List<EncryptedKey> generateList(@Nonnull final EncryptedData encData,
-            @Nonnull final EncryptedKeyResolver ekResolver) {
-        List<EncryptedKey> resolved = new ArrayList<>();
-        for (EncryptedKey encKey : ekResolver.resolve(encData)) {
-            resolved.add(encKey);
-        }
-        return resolved;
+            @Nonnull final EncryptedKeyResolver ekResolver, @Nullable final Set<String> recipients) {
+        
+        return StreamSupport.stream(ekResolver.resolve(encData, recipients).spliterator(), false)
+                .collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
     }
 
 }
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleKeyInfoReferenceEncryptedKeyResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleKeyInfoReferenceEncryptedKeyResolverTest.java
index 6da63d532..ecbebee51 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleKeyInfoReferenceEncryptedKeyResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleKeyInfoReferenceEncryptedKeyResolverTest.java
@@ -19,8 +19,12 @@ package org.opensaml.xmlsec.encryption.support.tests;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.core.testing.XMLObjectBaseTestCase;
 import org.opensaml.core.xml.XMLObject;
@@ -64,7 +68,7 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolverTest extends XMLObjectBas
         
         resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, null);
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -88,9 +92,9 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolverTest extends XMLObjectBas
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -114,9 +118,9 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolverTest extends XMLObjectBas
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -143,9 +147,37 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolverTest extends XMLObjectBas
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver(CollectionSupport.setOf("foo", "baz"));
+        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.setOf("foo", "baz"));
+        Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
+        
+        Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(1) == allKeys.get(2), "Unexpected EncryptedKey instance found");
+    }
+    
+    /** Multi recipient specified to resolver via ctor and method args. */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testMultiRecipientsCtorAndArgs() {
+        final String filename =  "/org/opensaml/xmlsec/encryption/support/SimpleKeyInfoReferenceEncryptedKeyResolverMultiple.xml";
+        final SignableSimpleXMLObject sxo =  (SignableSimpleXMLObject) unmarshallElement(filename);
+        assert sxo != null;
+        Assert.assertNotNull(sxo.getSimpleXMLObjects().get(0));
+        Assert.assertNotNull(sxo.getSimpleXMLObjects().get(0).getEncryptedData());
+        
+        final EncryptedData encData = sxo.getSimpleXMLObjects().get(0).getEncryptedData();
+        assert encData != null;
+        final KeyInfo keyInfo = encData.getKeyInfo();
+        assert keyInfo != null;
+        Assert.assertFalse(keyInfo.getKeyInfoReferences().isEmpty());
+        
+        List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
+        Assert.assertFalse(allKeys.isEmpty());
+        
+        resolver = new SimpleKeyInfoReferenceEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("baz"));
         Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -167,21 +199,20 @@ public class SimpleKeyInfoReferenceEncryptedKeyResolverTest extends XMLObjectBas
         }
         return allKeys;
     }
-
+    
     /**
      * Resolve EncryptedKeys and put them in an ordered list.
      * 
      * @param encData the EncryptedData context
      * @param ekResolver the resolver to test
+     * @param recipients the valid recipients for resolution
      * @return list of resolved EncryptedKeys
      */
     @Nonnull private List<EncryptedKey> generateList(@Nonnull final EncryptedData encData,
-            @Nonnull final EncryptedKeyResolver ekResolver) {
-        List<EncryptedKey> resolved = new ArrayList<>();
-        for (EncryptedKey encKey : ekResolver.resolve(encData)) {
-            resolved.add(encKey);
-        }
-        return resolved;
+            @Nonnull final EncryptedKeyResolver ekResolver, @Nullable final Set<String> recipients) {
+        
+        return StreamSupport.stream(ekResolver.resolve(encData, recipients).spliterator(), false)
+                .collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
     }
 
 }
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleRetrievalMethodEncryptedKeyResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleRetrievalMethodEncryptedKeyResolverTest.java
index ba12d2209..a0d18a915 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleRetrievalMethodEncryptedKeyResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleRetrievalMethodEncryptedKeyResolverTest.java
@@ -17,17 +17,14 @@
 
 package org.opensaml.xmlsec.encryption.support.tests;
 
-import org.testng.annotations.Test;
-
-import net.shibboleth.shared.collection.CollectionSupport;
-
-import org.testng.Assert;
-
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.core.testing.XMLObjectBaseTestCase;
 import org.opensaml.core.xml.XMLObject;
@@ -37,6 +34,10 @@ import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
 import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;
 import org.opensaml.xmlsec.mock.SignableSimpleXMLObject;
 import org.opensaml.xmlsec.signature.KeyInfo;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.collection.CollectionSupport;
 
 /**
  * Test the encrypted key resolver which dereferences RetrievalMethods.
@@ -66,7 +67,7 @@ public class SimpleRetrievalMethodEncryptedKeyResolverTest extends XMLObjectBase
         
         resolver = new SimpleRetrievalMethodEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, null);
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -90,9 +91,9 @@ public class SimpleRetrievalMethodEncryptedKeyResolverTest extends XMLObjectBase
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleRetrievalMethodEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new SimpleRetrievalMethodEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -117,9 +118,9 @@ public class SimpleRetrievalMethodEncryptedKeyResolverTest extends XMLObjectBase
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleRetrievalMethodEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new SimpleRetrievalMethodEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 0, "Incorrect number of resolved EncryptedKeys found");
     }
     
@@ -142,9 +143,9 @@ public class SimpleRetrievalMethodEncryptedKeyResolverTest extends XMLObjectBase
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleRetrievalMethodEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        resolver = new SimpleRetrievalMethodEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("foo"));
         Assert.assertEquals(resolved.size(), 1, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -169,9 +170,37 @@ public class SimpleRetrievalMethodEncryptedKeyResolverTest extends XMLObjectBase
         List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
         Assert.assertFalse(allKeys.isEmpty());
         
-        resolver = new SimpleRetrievalMethodEncryptedKeyResolver(CollectionSupport.setOf("foo", "baz"));
+        resolver = new SimpleRetrievalMethodEncryptedKeyResolver();
         
-        List<EncryptedKey> resolved = generateList(encData, resolver);
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.setOf("foo", "baz"));
+        Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
+        
+        Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
+        Assert.assertTrue(resolved.get(1) == allKeys.get(2), "Unexpected EncryptedKey instance found");
+    }
+    
+    /** Multi recipient specified to resolver via ctor and method args. */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testMultiRecipientsCtorAndArgs() {
+        String filename =  "/org/opensaml/xmlsec/encryption/support/SimpleRetrievalMethodEncryptedKeyResolverMultiple.xml";
+        final SignableSimpleXMLObject sxo =  (SignableSimpleXMLObject) unmarshallElement(filename);
+        assert sxo != null;
+        Assert.assertNotNull(sxo.getSimpleXMLObjects().get(0));
+        Assert.assertNotNull(sxo.getSimpleXMLObjects().get(0).getEncryptedData());
+        
+        final EncryptedData encData = sxo.getSimpleXMLObjects().get(0).getEncryptedData();
+        assert encData != null;
+        final KeyInfo keyInfo = encData.getKeyInfo();
+        assert keyInfo != null;
+        Assert.assertFalse(keyInfo.getRetrievalMethods().isEmpty());
+        
+        List<EncryptedKey> allKeys = getEncryptedKeys(sxo);
+        Assert.assertFalse(allKeys.isEmpty());
+        
+        resolver = new SimpleRetrievalMethodEncryptedKeyResolver(CollectionSupport.singleton("foo"));
+        
+        List<EncryptedKey> resolved = generateList(encData, resolver, CollectionSupport.singleton("baz"));
         Assert.assertEquals(resolved.size(), 2, "Incorrect number of resolved EncryptedKeys found");
         
         Assert.assertTrue(resolved.get(0) == allKeys.get(0), "Unexpected EncryptedKey instance found");
@@ -193,21 +222,20 @@ public class SimpleRetrievalMethodEncryptedKeyResolverTest extends XMLObjectBase
         }
         return allKeys;
     }
-
+    
     /**
      * Resolve EncryptedKeys and put them in an ordered list.
      * 
      * @param encData the EncryptedData context
      * @param ekResolver the resolver to test
+     * @param recipients the valid recipients for resolution
      * @return list of resolved EncryptedKeys
      */
     @Nonnull private List<EncryptedKey> generateList(@Nonnull final EncryptedData encData,
-            @Nonnull final EncryptedKeyResolver ekResolver) {
-        List<EncryptedKey> resolved = new ArrayList<>();
-        for (EncryptedKey encKey : ekResolver.resolve(encData)) {
-            resolved.add(encKey);
-        }
-        return resolved;
+            @Nonnull final EncryptedKeyResolver ekResolver, @Nullable final Set<String> recipients) {
+        
+        return StreamSupport.stream(ekResolver.resolve(encData, recipients).spliterator(), false)
+                .collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
     }
 
 
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolverTest.java
index cdc803ab2..efd0be190 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicDecryptionParametersResolverTest.java
@@ -22,6 +22,7 @@ import static org.testng.Assert.assertNull;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import net.shibboleth.shared.logic.ConstraintViolationException;
 import net.shibboleth.shared.resolver.CriteriaSet;
@@ -29,6 +30,7 @@ import net.shibboleth.shared.resolver.ResolverException;
 
 import org.opensaml.xmlsec.DecryptionParameters;
 import org.opensaml.xmlsec.criterion.DecryptionConfigurationCriterion;
+import org.opensaml.xmlsec.criterion.DecryptionRecipientsCriterion;
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
 import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
 import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
@@ -153,6 +155,57 @@ public class BasicDecryptionParametersResolverTest {
         Assert.assertTrue(encKeyResolver == controlEncKeyResolver3);
     }
     
+    @Test
+    public void testNoRecipients() throws ResolverException {
+        final DecryptionParameters params = resolver.resolveSingle(criteriaSet);
+        assert params != null;
+
+        Assert.assertNull(params.getRecipients());
+    }
+    
+    @Test
+    public void testRecipientsFromConfig() throws ResolverException {
+        config1.setRecipients(Set.of("A", "B", "C"));
+        
+        final DecryptionParameters params = resolver.resolveSingle(criteriaSet);
+        assert params != null;
+        
+        final Set<String> recipients = params.getRecipients();
+        assert recipients != null;
+
+        Assert.assertNotNull(recipients);
+        Assert.assertTrue(recipients.equals(Set.of("A", "B", "C")));
+    }
+    
+    @Test
+    public void testRecipientsFromCriterion() throws ResolverException {
+        criteriaSet.add(new DecryptionRecipientsCriterion(Set.of("X", "Y", "Z")));
+        
+        final DecryptionParameters params = resolver.resolveSingle(criteriaSet);
+        assert params != null;
+
+        final Set<String> recipients = params.getRecipients();
+        assert recipients != null;
+
+        Assert.assertNotNull(recipients);
+        Assert.assertTrue(recipients.equals(Set.of("X", "Y", "Z")));
+    }
+    
+    @Test
+    public void testRecipientsFromConfigAndCriterion() throws ResolverException {
+        config2.setRecipients(Set.of("A", "B", "C"));
+        criteriaSet.add(new DecryptionRecipientsCriterion(Set.of("X", "Y", "Z")));
+        
+        final DecryptionParameters params = resolver.resolveSingle(criteriaSet);
+        assert params != null;
+
+        final Set<String> recipients = params.getRecipients();
+        assert recipients != null;
+
+        Assert.assertNotNull(recipients);
+        Assert.assertTrue(recipients.equals(Set.of("A", "B", "C", "X", "Y", "Z")));
+    }
+    
     @Test
     public void testResolve() throws ResolverException {
         config1.setExcludedAlgorithms(List.of("foo", "bar"));

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


More information about the commits mailing list