keycloak-aplcache

Changes

Details

diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
index 682f179..4d77c64 100644
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
@@ -51,10 +51,12 @@ import org.keycloak.saml.SAML2AuthnRequestBuilder;
 import org.keycloak.saml.SAMLRequestParser;
 import org.keycloak.saml.SignatureAlgorithm;
 import org.keycloak.saml.common.constants.GeneralConstants;
+import org.keycloak.saml.common.constants.JBossSAMLConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ConfigurationException;
 import org.keycloak.saml.common.exceptions.ProcessingException;
 import org.keycloak.saml.common.util.Base64;
+import org.keycloak.saml.common.util.DocumentUtil;
 import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
@@ -72,10 +74,14 @@ import java.security.PublicKey;
 import java.security.Signature;
 import java.security.SignatureException;
 import java.util.*;
+
+import javax.xml.namespace.QName;
+
 import org.keycloak.dom.saml.v2.SAML2Object;
 import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
 import org.keycloak.rotation.KeyLocator;
 import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
+import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
 
 /**
  *
@@ -208,7 +214,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
                         return AuthOutcome.FAILED;
                     }
                 }
-                return handleLoginResponse((ResponseType) statusResponse, postBinding, onCreateSession);
+                return handleLoginResponse(holder, postBinding, onCreateSession);
             } finally {
                 sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
             }
@@ -310,7 +316,8 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
         return false;
     }
 
-    protected AuthOutcome handleLoginResponse(final ResponseType responseType, boolean postBinding, OnSessionCreated onCreateSession) {
+    protected AuthOutcome handleLoginResponse(SAMLDocumentHolder responseHolder, boolean postBinding, OnSessionCreated onCreateSession) {
+    	final ResponseType responseType = (ResponseType) responseHolder.getSamlObject();
         AssertionType assertion = null;
         if (! isSuccessfulSamlResponse(responseType) || responseType.getAssertions() == null || responseType.getAssertions().isEmpty()) {
             challenge = new AuthChallenge() {
@@ -355,14 +362,33 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
 
         if (deployment.getIDP().getSingleSignOnService().validateAssertionSignature()) {
             try {
-                validateSamlSignature(new SAMLDocumentHolder(AssertionUtil.asDocument(assertion)), postBinding, GeneralConstants.SAML_RESPONSE_KEY);
-            } catch (VerificationException e) {
-                log.error("Failed to verify saml assertion signature", e);
+                if (!AssertionUtil.isSignatureValid(getAssertionFromResponse(responseHolder), deployment.getIDP().getSignatureValidationKeyLocator())) {
+                    log.error("Failed to verify saml assertion signature");
+
+                    challenge = new AuthChallenge() {
+
+                        @Override
+                        public boolean challenge(HttpFacade exchange) {
+                            SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE, responseType);
+                            exchange.getRequest().setError(error);
+                            exchange.getResponse().sendError(403);
+                            return true;
+                        }
 
+                        @Override
+                        public int getResponseCode() {
+                            return 403;
+                        }
+                    };
+                    return AuthOutcome.FAILED;
+                }
+            } catch (Exception e) {
+                log.error("Error processing validation of SAML assertion: " + e.getMessage());
                 challenge = new AuthChallenge() {
+
                     @Override
                     public boolean challenge(HttpFacade exchange) {
-                        SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE, responseType);
+                        SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.EXTRACTION_FAILURE);
                         exchange.getRequest().setError(error);
                         exchange.getResponse().sendError(403);
                         return true;
@@ -374,8 +400,6 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
                     }
                 };
                 return AuthOutcome.FAILED;
-            } catch (ProcessingException e) {
-                e.printStackTrace();
             }
         }
 
@@ -473,6 +497,18 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
           && Objects.equals(responseType.getStatus().getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());
     }
 
+    private Element getAssertionFromResponse(final SAMLDocumentHolder responseHolder) throws ConfigurationException, ProcessingException {
+        Element encryptedAssertion = DocumentUtil.getElement(responseHolder.getSamlDocument(), new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
+        if (encryptedAssertion != null) {
+            // encrypted assertion.
+            // We'll need to decrypt it first.
+            Document encryptedAssertionDocument = DocumentUtil.createDocument();
+            encryptedAssertionDocument.appendChild(encryptedAssertionDocument.importNode(encryptedAssertion, true));
+            return XMLEncryptionUtil.decryptElementInDocument(encryptedAssertionDocument, deployment.getDecryptionKey());
+        }
+        return DocumentUtil.getElement(responseHolder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));
+    }
+
     private String getAttributeValue(Object attrValue) {
         String value = null;
         if (attrValue instanceof String) {
diff --git a/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java b/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java
index b5f2232..f516124 100755
--- a/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java
@@ -52,6 +52,7 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.Objects;
 
 /**
  * Utility dealing with DOM
@@ -554,4 +555,33 @@ public class DocumentUtil {
 
         return documentBuilderFactory;
     }
+
+    /**
+     * Get a (direct) child {@linkplain Element} from the parent {@linkplain Element}. 
+     *
+     * @param parent parent element
+     * @param targetNamespace namespace URI
+     * @param targetLocalName local name
+     * @return a child element matching the target namespace and localname, where {@linkplain Element#getParentNode()} is the parent input parameter
+     * @return
+     */
+    
+    public static Element getDirectChildElement(Element parent, String targetNamespace, String targetLocalName) {
+        Node child = parent.getFirstChild();
+        
+        while(child != null) {
+            if(child instanceof Element) {
+                Element childElement = (Element)child;
+                
+                String ns = childElement.getNamespaceURI();
+                String localName = childElement.getLocalName();
+                
+                if(Objects.equals(targetNamespace, ns) && Objects.equals(targetLocalName, localName)) {
+                    return childElement;
+                }
+            }
+            child = child.getNextSibling();
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
index ef3e3bd..404c074 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
@@ -210,9 +210,9 @@ public class SAML2Signature {
      *
      * @param document SAML document to have its ID attribute configured.
      */
-    private void configureIdAttribute(Document document) {
+    public static void configureIdAttribute(Document document) {
         // Estabilish the IDness of the ID attribute.
-        document.getDocumentElement().setIdAttribute(ID_ATTRIBUTE_NAME, true);
+        configureIdAttribute(document.getDocumentElement());
 
         NodeList nodes = document.getElementsByTagNameNS(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
                 JBossSAMLConstants.ASSERTION.get());
@@ -220,8 +220,13 @@ public class SAML2Signature {
         for (int i = 0; i < nodes.getLength(); i++) {
             Node n = nodes.item(i);
             if (n instanceof Element) {
-                ((Element) n).setIdAttribute(ID_ATTRIBUTE_NAME, true);
+                configureIdAttribute((Element) n);
             }
         }
     }
+
+    public static void configureIdAttribute(Element element) {
+        element.setIdAttribute(JBossSAMLConstants.ID.get(), true);
+    }
+
 }
\ No newline at end of file
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
index 714ee3f..244fb7d 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
@@ -32,6 +32,7 @@ import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
 import org.keycloak.dom.saml.v2.assertion.SubjectType;
 import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType;
 import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.rotation.KeyLocator;
 import org.keycloak.saml.common.ErrorCodes;
 import org.keycloak.saml.common.PicketLinkLogger;
 import org.keycloak.saml.common.PicketLinkLoggerFactory;
@@ -48,11 +49,12 @@ import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
 import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter;
 import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
 import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
-
+import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
+import javax.xml.crypto.dsig.XMLSignature;
 import javax.xml.datatype.XMLGregorianCalendar;
 import javax.xml.namespace.QName;
 import java.io.ByteArrayInputStream;
@@ -266,26 +268,56 @@ public class AssertionUtil {
     }
 
     /**
-     * Given an assertion element, validate the signature
+     * Given an {@linkplain Element}, validate the Signature direct child element
      *
-     * @param assertionElement
+     * @param element parent {@linkplain Element}
      * @param publicKey the {@link PublicKey}
      *
-     * @return
+     * @return true if signature is present and valid
      */
-    public static boolean isSignatureValid(Element assertionElement, PublicKey publicKey) {
-        try {
-            Document doc = DocumentUtil.createDocument();
-            Node n = doc.importNode(assertionElement, true);
-            doc.appendChild(n);
+    public static boolean isSignatureValid(Element element, PublicKey publicKey) {
+        return isSignatureValid(element, new HardcodedKeyLocator(publicKey));
+    }
 
-            return new SAML2Signature().validate(doc, new HardcodedKeyLocator(publicKey));
+    /**
+     * Given an {@linkplain Element}, validate the Signature direct child element
+     *
+     * @param element parent {@linkplain Element}
+     * @param keyLocator the {@link KeyLocator}
+     *
+     * @return true if signature is present and valid
+     */
+    
+    public static boolean isSignatureValid(Element element, KeyLocator keyLocator) {
+        try {
+            SAML2Signature.configureIdAttribute(element);
+            
+            Element signature = getSignature(element);
+            if(signature != null) {
+                return XMLSignatureUtil.validateSingleNode(signature, keyLocator);
+            }
         } catch (Exception e) {
             logger.signatureAssertionValidationError(e);
         }
         return false;
     }
+    
+    /**
+     * 
+     * Given an {@linkplain Element}, check if there is a Signature direct child element
+     * 
+     * @param element parent {@linkplain Element}
+     * @return true if signature is present
+     */
 
+    public static boolean isSignedElement(Element element) {
+        return getSignature(element) != null;
+    }
+    
+    protected static Element getSignature(Element element) {
+        return DocumentUtil.getDirectChildElement(element, XMLSignature.XMLNS, "Signature");
+    }
+    
     /**
      * Check whether the assertion has expired
      *
@@ -540,7 +572,23 @@ public class AssertionUtil {
         return responseType.getAssertions().get(0).getAssertion();
     }
 
-    public static ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
+    public static boolean isAssertionEncrypted(ResponseType responseType) throws ProcessingException {
+        List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
+
+        if (assertions.isEmpty()) {
+            throw new ProcessingException("No assertion from response.");
+        }
+
+        ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
+        return rtChoiceType.getEncryptedAssertion() != null;
+    }
+
+    /**
+     * This method modifies the given responseType, and replaces the encrypted assertion with a decrypted version.
+     * @param responseType a response containg an encrypted assertion
+     * @return the assertion element as it was decrypted. This can be used in signature verification.
+     */
+    public static Element decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
         SAML2Response saml2Response = new SAML2Response();
 
         Document doc = saml2Response.convert(responseType);
@@ -564,6 +612,6 @@ public class AssertionUtil {
 
         responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
 
-        return responseType;
+        return decryptedDocumentElement;
     }
 }
\ No newline at end of file
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
index 53228c9..7093a20 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
@@ -468,7 +468,7 @@ public class XMLSignatureUtil {
         return true;
     }
 
-    private static boolean validateSingleNode(Node signatureNode, final KeyLocator locator) throws MarshalException, XMLSignatureException {
+    public static boolean validateSingleNode(Node signatureNode, final KeyLocator locator) throws MarshalException, XMLSignatureException {
         KeySelectorUtilizingKeyNameHint sel = new KeySelectorUtilizingKeyNameHint(locator);
         try {
             if (validateUsingKeySelector(signatureNode, sel)) {
diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
index 52dd7ba..713a5bd 100644
--- a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
+++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
@@ -103,9 +103,9 @@ public class SAMLParserTest {
             assertNotNull(rtChoiceType.getEncryptedAssertion());
 
             PrivateKey privateKey = DerUtils.decodePrivateKey(Base64.decode(PRIVATE_KEY));
-            ResponseType rtWithDecryptedAssertion = AssertionUtil.decryptAssertion(resp, privateKey);
+            AssertionUtil.decryptAssertion(resp, privateKey);
 
-            rtChoiceType = rtWithDecryptedAssertion.getAssertions().get(0);
+            rtChoiceType = resp.getAssertions().get(0);
             assertNotNull(rtChoiceType.getAssertion());
             assertNull(rtChoiceType.getEncryptedAssertion());
         }
diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java
new file mode 100644
index 0000000..b60bb4b
--- /dev/null
+++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java
@@ -0,0 +1,58 @@
+package org.keycloak.saml.processing.core.saml.v2.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.util.Arrays;
+import org.junit.Test;
+import org.keycloak.common.util.Base64;
+import org.keycloak.common.util.DerUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class AssertionUtilTest {
+
+    private static final String PRIVATE_KEY = "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==";
+
+    /**
+     * The public certificate that corresponds to {@link #PRIVATE_KEY}.
+     */
+    private static final String PUBLIC_CERT = "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin";
+
+    @Test
+    public void testSaml20Signed() throws Exception {
+        
+        X509Certificate decodeCertificate = DerUtils.decodeCertificate(new ByteArrayInputStream(Base64.decode(PUBLIC_CERT)));
+        
+        try (InputStream st = AssertionUtilTest.class.getResourceAsStream("saml20-signed-response.xml")) {
+            Document document = DocumentUtil.getDocument(st);
+            
+            Element assertion = DocumentUtil.getDirectChildElement(document.getDocumentElement(), "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion");
+            
+            assertTrue(AssertionUtil.isSignatureValid(assertion, decodeCertificate.getPublicKey()));
+            
+            // test manipulation of signature
+            Element signatureElement = AssertionUtil.getSignature(assertion);
+            byte[] validSignature = Base64.decode(signatureElement.getTextContent());
+            
+            // change the signature value slightly
+            byte[] invalidSignature = Arrays.clone(validSignature);
+            invalidSignature[0] ^= invalidSignature[0];
+            signatureElement.setTextContent(Base64.encodeBytes(invalidSignature));
+            
+            // check that signature now is invalid
+            assertFalse(AssertionUtil.isSignatureValid(document.getDocumentElement(), decodeCertificate.getPublicKey()));
+            
+            // restore valid signature, but remove Signature element, check that still invalid
+            signatureElement.setTextContent(Base64.encodeBytes(validSignature));
+
+            assertion.removeChild(signatureElement);
+            assertFalse(AssertionUtil.isSignatureValid(document.getDocumentElement(), decodeCertificate.getPublicKey()));
+        }
+    }
+
+}
diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/saml/v2/util/saml20-signed-response.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/saml/v2/util/saml20-signed-response.xml
new file mode 100644
index 0000000..998520a
--- /dev/null
+++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/saml/v2/util/saml20-signed-response.xml
@@ -0,0 +1 @@
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider/endpoint" ID="ID_9aca1381-6265-434c-98a5-d89236d32ea0" InResponseTo="ID_2d81575a-0bde-488a-b69d-0b78c0fcf521" IssueInstant="2017-05-04T22:18:13.550Z" Version="2.0"><saml:Issuer>http://localhost:8080/auth/realms/saml-broker-realm</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="ID_e9d22a60-7954-48b2-ad97-700d70e332f5" IssueInstant="2017-05-04T22:18:13.546Z" Version="2.0"><saml:Issuer>http://localhost:8080/auth/realms/saml-broker-realm</saml:Issuer><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><dsig:Reference URI="#ID_e9d22a60-7954-48b2-ad97-700d70e332f5"><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><dsig:DigestValue>cCSNXxLmu411weW1kRpie4C9yaBg2In6V4oEuqya0Eo=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>Qe6ZqgSwFH31UTu+zHqr1/UsafH0luxP5OH/cqyHm07Kf/Fp/fm9mnHJ0kGoUn0SUo7xWvwy8AzUfPXWMYS3kDyhUsPzgz0CnCzzfTz3koKFczgyIQ8sokIDv0cTp3z1qCUVWV0CEPzhtWlaIus2W89TEi/h9KjYrkeGl3+cpm8BPEAt4EP8Oht5czK2haIfPMDUm5Y7uw/FCSsvSfFyrlJ0jR/YMeP9PP0InYYegI9QQgvXKRm6DZSNZgKYFpprc12v6vv/zTaMm5fbuuy1wNDuDTB8EF6K1yrq21DatJXUKE1oOMBrkOvbFJNtgHlQviz1OssAqzHlf0NQPIAEig==</dsig:SignatureValue><dsig:KeyInfo><dsig:KeyName>IzH2UxfMxovYTEHn4Bh-EAj-Zrvldukl_5Snu0RA0B8</dsig:KeyName><dsig:X509Data><dsig:X509Certificate>MIICsTCCAZkCBgFb1GERYzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFzYW1sLWJyb2tlci1yZWFsbTAeFw0xNzA1MDQxNjUxMjJaFw0yNzA1MDQxNjUzMDJaMBwxGjAYBgNVBAMMEXNhbWwtYnJva2VyLXJlYWxtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCBuWKmcVPX4sZtpvBp8BgQwH51l5tKPuQq66/4JH40hzkrLOGcVEOafIsGoOWi8HsZWZu+APwhSrnRNd7yMV+NJyw5W1DKjhdUPWnzPNy+UMAcoUBFhXDIWog0qxgGvTdoe1lfHryUQt1cd95SFVIerJA93nFbSOoMB+N7TmfQm+sNu2pJ2tr6mx3wGCXMnWf29gwhCI3wV19hh4KugnMIEStjvQoRyh2yna64BrR3eaUyhU/Bdrq2VXLNU/9WXg9gbRLUEWkMUPKOeQ5cGCgc4JFyFXRo5ExkzmvP9vwBRtjQulk5QKqfYo251mKvTQgO7K8d4CzVS/4+bpgKvZAM</dsig:X509Certificate></dsig:X509Data><dsig:KeyValue><dsig:RSAKeyValue><dsig:Modulus>gj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQ==</dsig:Modulus><dsig:Exponent>AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo></dsig:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">G-ebb3a66f-686f-4bb9-8a8b-20b566ca747b</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="ID_2d81575a-0bde-488a-b69d-0b78c0fcf521" NotOnOrAfter="2017-05-04T22:23:11.546Z" Recipient="http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider/endpoint"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2017-05-04T22:18:11.546Z" NotOnOrAfter="2017-05-04T22:19:11.546Z"><saml:AudienceRestriction><saml:Audience>http://localhost:8080/auth/realms/saml-broker-authentication-realm</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2017-05-04T22:18:13.551Z" SessionIndex="c0cc880f-83d7-461b-b299-9fb3354f598c"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">manager</saml:AttributeValue></saml:Attribute><saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index 38e57cb..af93f28 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -52,6 +52,7 @@ import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ConfigurationException;
 import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.common.util.DocumentUtil;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
@@ -83,8 +84,12 @@ import java.util.List;
 import org.keycloak.rotation.HardcodedKeyLocator;
 import org.keycloak.rotation.KeyLocator;
 import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
 
 import java.util.*;
+import javax.xml.crypto.dsig.XMLSignature;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -478,6 +483,17 @@ public class SAMLEndpoint {
     protected class PostBinding extends Binding {
         @Override
         protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
+            NodeList nl = documentHolder.getSamlDocument().getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
+            boolean anyElementSigned = (nl != null && nl.getLength() > 0);
+            if ((! anyElementSigned) && (documentHolder.getSamlObject() instanceof ResponseType)) {
+                ResponseType responseType = (ResponseType) documentHolder.getSamlObject();
+                List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
+                if (! assertions.isEmpty() ) {
+                    // Only relax verification if the response is an authnresponse and contains (encrypted/plaintext) assertion.
+                    // In that case, signature is validated on assertion element
+                    return;
+                }
+            }
             SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKeyLocator());
         }
 
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
index f07495f..46d8b95 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
@@ -27,6 +27,24 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
 
     public static final XmlKeyInfoKeyNameTransformer DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER = XmlKeyInfoKeyNameTransformer.NONE;
 
+    public static final String ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO = "addExtensionsElementWithKeyInfo";
+    public static final String BACKCHANNEL_SUPPORTED = "backchannelSupported";
+    public static final String ENCRYPTION_PUBLIC_KEY = "encryptionPublicKey";
+    public static final String FORCE_AUTHN = "forceAuthn";
+    public static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
+    public static final String POST_BINDING_AUTHN_REQUEST = "postBindingAuthnRequest";
+    public static final String POST_BINDING_LOGOUT = "postBindingLogout";
+    public static final String POST_BINDING_RESPONSE = "postBindingResponse";
+    public static final String SIGNATURE_ALGORITHM = "signatureAlgorithm";
+    public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate";
+    public static final String SINGLE_LOGOUT_SERVICE_URL = "singleLogoutServiceUrl";
+    public static final String SINGLE_SIGN_ON_SERVICE_URL = "singleSignOnServiceUrl";
+    public static final String VALIDATE_SIGNATURE = "validateSignature";
+    public static final String WANT_ASSERTIONS_ENCRYPTED = "wantAssertionsEncrypted";
+    public static final String WANT_ASSERTIONS_SIGNED = "wantAssertionsSigned";
+    public static final String WANT_AUTHN_REQUESTS_SIGNED = "wantAuthnRequestsSigned";
+    public static final String XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER = "xmlSigKeyInfoKeyNameTransformer";
+
     public SAMLIdentityProviderConfig() {
     }
 
@@ -35,35 +53,35 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
     }
 
     public String getSingleSignOnServiceUrl() {
-        return getConfig().get("singleSignOnServiceUrl");
+        return getConfig().get(SINGLE_SIGN_ON_SERVICE_URL);
     }
 
     public void setSingleSignOnServiceUrl(String singleSignOnServiceUrl) {
-        getConfig().put("singleSignOnServiceUrl", singleSignOnServiceUrl);
+        getConfig().put(SINGLE_SIGN_ON_SERVICE_URL, singleSignOnServiceUrl);
     }
 
     public String getSingleLogoutServiceUrl() {
-        return getConfig().get("singleLogoutServiceUrl");
+        return getConfig().get(SINGLE_LOGOUT_SERVICE_URL);
     }
 
     public void setSingleLogoutServiceUrl(String singleLogoutServiceUrl) {
-        getConfig().put("singleLogoutServiceUrl", singleLogoutServiceUrl);
+        getConfig().put(SINGLE_LOGOUT_SERVICE_URL, singleLogoutServiceUrl);
     }
 
     public boolean isValidateSignature() {
-        return Boolean.valueOf(getConfig().get("validateSignature"));
+        return Boolean.valueOf(getConfig().get(VALIDATE_SIGNATURE));
     }
 
     public void setValidateSignature(boolean validateSignature) {
-        getConfig().put("validateSignature", String.valueOf(validateSignature));
+        getConfig().put(VALIDATE_SIGNATURE, String.valueOf(validateSignature));
     }
 
     public boolean isForceAuthn() {
-        return Boolean.valueOf(getConfig().get("forceAuthn"));
+        return Boolean.valueOf(getConfig().get(FORCE_AUTHN));
     }
 
     public void setForceAuthn(boolean forceAuthn) {
-        getConfig().put("forceAuthn", String.valueOf(forceAuthn));
+        getConfig().put(FORCE_AUTHN, String.valueOf(forceAuthn));
     }
 
     /**
@@ -103,70 +121,68 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
         return crt.split(",");
     }
 
-    public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate";
-
     public String getNameIDPolicyFormat() {
-        return getConfig().get("nameIDPolicyFormat");
+        return getConfig().get(NAME_ID_POLICY_FORMAT);
     }
 
     public void setNameIDPolicyFormat(String nameIDPolicyFormat) {
-        getConfig().put("nameIDPolicyFormat", nameIDPolicyFormat);
+        getConfig().put(NAME_ID_POLICY_FORMAT, nameIDPolicyFormat);
     }
 
     public boolean isWantAuthnRequestsSigned() {
-        return Boolean.valueOf(getConfig().get("wantAuthnRequestsSigned"));
+        return Boolean.valueOf(getConfig().get(WANT_AUTHN_REQUESTS_SIGNED));
     }
 
     public void setWantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) {
-        getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
+        getConfig().put(WANT_AUTHN_REQUESTS_SIGNED, String.valueOf(wantAuthnRequestsSigned));
     }
 
     public boolean isAddExtensionsElementWithKeyInfo() {
-        return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo"));
+        return Boolean.valueOf(getConfig().get(ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
     }
 
     public void setAddExtensionsElementWithKeyInfo(boolean addExtensionsElementWithKeyInfo) {
-        getConfig().put("addExtensionsElementWithKeyInfo", String.valueOf(addExtensionsElementWithKeyInfo));
+        getConfig().put(ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO, String.valueOf(addExtensionsElementWithKeyInfo));
     }
 
     public String getSignatureAlgorithm() {
-        return getConfig().get("signatureAlgorithm");
+        return getConfig().get(SIGNATURE_ALGORITHM);
     }
 
     public void setSignatureAlgorithm(String signatureAlgorithm) {
-        getConfig().put("signatureAlgorithm", signatureAlgorithm);
+        getConfig().put(SIGNATURE_ALGORITHM, signatureAlgorithm);
     }
 
     public String getEncryptionPublicKey() {
-        return getConfig().get("encryptionPublicKey");
+        return getConfig().get(ENCRYPTION_PUBLIC_KEY);
     }
 
     public void setEncryptionPublicKey(String encryptionPublicKey) {
-        getConfig().put("encryptionPublicKey", encryptionPublicKey);
+        getConfig().put(ENCRYPTION_PUBLIC_KEY, encryptionPublicKey);
     }
 
     public boolean isPostBindingAuthnRequest() {
-        return Boolean.valueOf(getConfig().get("postBindingAuthnRequest"));
+        return Boolean.valueOf(getConfig().get(POST_BINDING_AUTHN_REQUEST));
     }
 
     public void setPostBindingAuthnRequest(boolean postBindingAuthnRequest) {
-        getConfig().put("postBindingAuthnRequest", String.valueOf(postBindingAuthnRequest));
+        getConfig().put(POST_BINDING_AUTHN_REQUEST, String.valueOf(postBindingAuthnRequest));
     }
 
     public boolean isPostBindingResponse() {
-        return Boolean.valueOf(getConfig().get("postBindingResponse"));
+        return Boolean.valueOf(getConfig().get(POST_BINDING_RESPONSE));
     }
 
     public void setPostBindingResponse(boolean postBindingResponse) {
-        getConfig().put("postBindingResponse", String.valueOf(postBindingResponse));
+        getConfig().put(POST_BINDING_RESPONSE, String.valueOf(postBindingResponse));
     }
 
     public boolean isBackchannelSupported() {
-        return Boolean.valueOf(getConfig().get("backchannelSupported"));
+        return Boolean.valueOf(getConfig().get(BACKCHANNEL_SUPPORTED));
     }
 
     public void setBackchannelSupported(boolean backchannel) {
-        getConfig().put("backchannelSupported", String.valueOf(backchannel));
+        getConfig().put(BACKCHANNEL_SUPPORTED, String.valueOf(backchannel));
     }
 
     /**
@@ -174,11 +190,11 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
      * @return Configured ransformer of {@link #DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER} if not set.
      */
     public XmlKeyInfoKeyNameTransformer getXmlSigKeyInfoKeyNameTransformer() {
-        return XmlKeyInfoKeyNameTransformer.from(getConfig().get("xmlSigKeyInfoKeyNameTransformer"), DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
+        return XmlKeyInfoKeyNameTransformer.from(getConfig().get(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER), DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
     }
 
     public void setXmlSigKeyInfoKeyNameTransformer(XmlKeyInfoKeyNameTransformer xmlSigKeyInfoKeyNameTransformer) {
-        getConfig().put("xmlSigKeyInfoKeyNameTransformer",
+        getConfig().put(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER,
           xmlSigKeyInfoKeyNameTransformer == null
             ? null
             : xmlSigKeyInfoKeyNameTransformer.name());
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index cabbcb4..2e1fea3 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -193,7 +193,7 @@ public class SamlProtocol implements LoginProtocol {
                     if (samlClient.requiresEncryption()) {
                         PublicKey publicKey;
                         try {
-                            publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
+                            publicKey = SamlProtocolUtils.getEncryptionKey(client);
                         } catch (Exception e) {
                             logger.error("failed", e);
                             return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
@@ -446,7 +446,7 @@ public class SamlProtocol implements LoginProtocol {
         if (samlClient.requiresEncryption()) {
             PublicKey publicKey = null;
             try {
-                publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
+                publicKey = SamlProtocolUtils.getEncryptionKey(client);
             } catch (Exception e) {
                 logger.error("failed", e);
                 return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
index 026a54a..7ab97a4 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
@@ -103,7 +103,7 @@ public class SamlProtocolUtils {
      * @return Public key for encryption.
      * @throws VerificationException
      */
-    public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
+    public static PublicKey getEncryptionKey(ClientModel client) throws VerificationException {
         return getPublicKey(client, SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncServlet.java
index 874b1e8..82a29fe 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncServlet.java
@@ -27,6 +27,7 @@ import java.net.URL;
  */
 public class SalesPostEncServlet extends SAMLServlet {
     public static final String DEPLOYMENT_NAME = "sales-post-enc";
+    public static final String CLIENT_NAME = "http://localhost:8081/sales-post-enc/";
 
     @ArquillianResource
     @OperateOnDeployment(DEPLOYMENT_NAME)
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncSignAssertionsOnlyServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncSignAssertionsOnlyServlet.java
new file mode 100644
index 0000000..cb44ac2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostEncSignAssertionsOnlyServlet.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class SalesPostEncSignAssertionsOnlyServlet extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "sales-post-enc-sign-assertions-only";
+    public static final String CLIENT_NAME = "http://localhost:8081/sales-post-enc-sign-assertions-only/";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java
new file mode 100644
index 0000000..0272a1b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java
@@ -0,0 +1,55 @@
+package org.keycloak.testsuite.updaters;
+
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import java.io.Closeable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class ClientAttributeUpdater {
+
+    private final Map<String, String> originalAttributes = new HashMap<>();
+
+    private final ClientResource clientResource;
+
+    private final ClientRepresentation rep;
+
+    public ClientAttributeUpdater(ClientResource clientResource) {
+        this.clientResource = clientResource;
+        this.rep = clientResource.toRepresentation();
+        if (this.rep.getAttributes() == null) {
+            this.rep.setAttributes(new HashMap<>());
+        }
+    }
+
+    public ClientAttributeUpdater setAttribute(String name, String value) {
+        if (! originalAttributes.containsKey(name)) {
+            this.originalAttributes.put(name, this.rep.getAttributes().put(name, value));
+        } else {
+            this.rep.getAttributes().put(name, value);
+        }
+        return this;
+    }
+
+    public ClientAttributeUpdater removeAttribute(String name) {
+        if (! originalAttributes.containsKey(name)) {
+            this.originalAttributes.put(name, this.rep.getAttributes().put(name, null));
+        } else {
+            this.rep.getAttributes().put(name, null);
+        }
+        return this;
+    }
+
+    public Closeable update() {
+        clientResource.update(rep);
+
+        return () -> {
+            rep.getAttributes().putAll(originalAttributes);
+            clientResource.update(rep);
+        };
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java
new file mode 100644
index 0000000..b8eb487
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java
@@ -0,0 +1,55 @@
+package org.keycloak.testsuite.updaters;
+
+import org.keycloak.admin.client.resource.IdentityProviderResource;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import java.io.Closeable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class IdentityProviderAttributeUpdater {
+
+    private final Map<String, String> originalAttributes = new HashMap<>();
+
+    private final IdentityProviderResource identityProviderResource;
+
+    private final IdentityProviderRepresentation rep;
+
+    public IdentityProviderAttributeUpdater(IdentityProviderResource identityProviderResource) {
+        this.identityProviderResource = identityProviderResource;
+        this.rep = identityProviderResource.toRepresentation();
+        if (this.rep.getConfig() == null) {
+            this.rep.setConfig(new HashMap<>());
+        }
+    }
+
+    public IdentityProviderAttributeUpdater setAttribute(String name, String value) {
+        if (! originalAttributes.containsKey(name)) {
+            this.originalAttributes.put(name, this.rep.getConfig().put(name, value));
+        } else {
+            this.rep.getConfig().put(name, value);
+        }
+        return this;
+    }
+
+    public IdentityProviderAttributeUpdater removeAttribute(String name) {
+        if (! originalAttributes.containsKey(name)) {
+            this.originalAttributes.put(name, this.rep.getConfig().put(name, null));
+        } else {
+            this.rep.getConfig().put(name, null);
+        }
+        return this;
+    }
+
+    public Closeable update() {
+        identityProviderResource.update(rep);
+
+        return () -> {
+            rep.getConfig().putAll(originalAttributes);
+            identityProviderResource.update(rep);
+        };
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java
new file mode 100644
index 0000000..72c2c4b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java
@@ -0,0 +1,55 @@
+package org.keycloak.testsuite.updaters;
+
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.RealmRepresentation;
+import java.io.Closeable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class RealmAttributeUpdater {
+
+    private final Map<String, String> originalAttributes = new HashMap<>();
+
+    private final RealmResource realmResource;
+
+    private final RealmRepresentation rep;
+
+    public RealmAttributeUpdater(RealmResource realmResource) {
+        this.realmResource = realmResource;
+        this.rep = realmResource.toRepresentation();
+        if (this.rep.getAttributes() == null) {
+            this.rep.setAttributes(new HashMap<>());
+        }
+    }
+
+    public RealmAttributeUpdater setAttribute(String name, String value) {
+        if (! originalAttributes.containsKey(name)) {
+            this.originalAttributes.put(name, this.rep.getAttributes().put(name, value));
+        } else {
+            this.rep.getAttributes().put(name, value);
+        }
+        return this;
+    }
+
+    public RealmAttributeUpdater removeAttribute(String name) {
+        if (! originalAttributes.containsKey(name)) {
+            this.originalAttributes.put(name, this.rep.getAttributes().put(name, null));
+        } else {
+            this.rep.getAttributes().put(name, null);
+        }
+        return this;
+    }
+
+    public Closeable update() {
+        realmResource.update(rep);
+
+        return () -> {
+            rep.getAttributes().putAll(originalAttributes);
+            realmResource.update(rep);
+        };
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/SetSystemProperty.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/SetSystemProperty.java
new file mode 100644
index 0000000..9a798b2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/SetSystemProperty.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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.keycloak.testsuite.updaters;
+
+import java.io.Closeable;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SetSystemProperty implements Closeable {
+
+    private final String name;
+    private final String oldValue;
+
+    public SetSystemProperty(String name, String value) {
+        this.name = name;
+        this.oldValue = System.getProperty(name);
+
+        if (value == null) {
+            if (oldValue != null) {
+                System.getProperties().remove(name);
+            }
+        } else {
+            System.setProperty(name, value);
+        }
+    }
+
+    public void revert() {
+        String value = System.getProperty(name);
+
+        if (oldValue == null) {
+            if (value != null) {
+                System.getProperties().remove(name);
+            }
+        } else {
+            System.setProperty(name, oldValue);
+        }
+    }
+
+    @Override
+    public void close() {
+        revert();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
index ce92fb8..70ef820 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
@@ -24,6 +24,7 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
         salesMetadataServletPage.checkRoles(true);
         salesPostServletPage.checkRoles(true);
         salesPostEncServletPage.checkRoles(true);
+        salesPostEncSignAssertionsOnlyServletPage.checkRoles(true);
         salesPostSigServletPage.checkRoles(true);
         salesPostPassiveServletPage.checkRoles(true);
         salesPostSigPersistentServletPage.checkRoles(true);
@@ -56,6 +57,7 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
         salesMetadataServletPage.checkRoles(false);
         salesPostServletPage.checkRoles(false);
         salesPostEncServletPage.checkRoles(false);
+        salesPostEncSignAssertionsOnlyServletPage.checkRoles(false);
         salesPostSigServletPage.checkRoles(false);
         salesPostPassiveServletPage.checkRoles(false);
         salesPostSigEmailServletPage.checkRoles(false);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
index 32d3a9b..2927ec8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.testsuite.adapter.servlet;
 
+import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpUriRequest;
@@ -88,14 +89,13 @@ import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Validator;
 import java.io.ByteArrayInputStream;
+import java.io.Closeable;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URL;
 import java.security.KeyPair;
 import java.security.PublicKey;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import static org.hamcrest.Matchers.*;
@@ -109,7 +109,6 @@ import static org.keycloak.testsuite.util.IOUtil.loadXML;
 import static org.keycloak.testsuite.util.IOUtil.modifyDocElementAttribute;
 import static org.keycloak.testsuite.util.Matchers.bodyHC;
 import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
-import static org.keycloak.testsuite.util.SamlClient.Binding.POST;
 import static org.keycloak.testsuite.util.SamlClient.idpInitiatedLogin;
 import static org.keycloak.testsuite.util.SamlClient.login;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
@@ -159,6 +158,9 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
     protected SalesPostEncServlet salesPostEncServletPage;
 
     @Page
+    protected SalesPostEncSignAssertionsOnlyServlet salesPostEncSignAssertionsOnlyServletPage;
+
+    @Page
     protected SalesPostPassiveServlet salesPostPassiveServletPage;
 
     @Page
@@ -261,6 +263,11 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         return samlServletDeployment(SalesPostEncServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
     }
 
+    @Deployment(name = SalesPostEncSignAssertionsOnlyServlet.DEPLOYMENT_NAME)
+    protected static WebArchive salesPostEncSignAssertionsOnly() {
+        return samlServletDeployment(SalesPostEncSignAssertionsOnlyServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
     @Deployment(name = SalesPostPassiveServlet.DEPLOYMENT_NAME)
     protected static WebArchive salesPostPassive() {
         return samlServletDeployment(SalesPostPassiveServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
@@ -628,6 +635,24 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
     }
 
     @Test
+    public void salesPostEncSignedAssertionsOnlyTest() throws Exception {
+        testSuccessfulAndUnauthorizedLogin(salesPostEncSignAssertionsOnlyServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void salesPostEncSignedAssertionsAndDocumentTest() throws Exception {
+        ClientRepresentation salesPostEncClient = testRealmResource().clients().findByClientId(SalesPostEncServlet.CLIENT_NAME).get(0);
+        try (Closeable client = new ClientAttributeUpdater(testRealmResource().clients().get(salesPostEncClient.getId()))
+          .setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "true")
+          .setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "true")
+          .update()) {
+            testSuccessfulAndUnauthorizedLogin(salesPostEncServletPage, testRealmSAMLPostLoginPage);
+        } finally {
+            salesPostEncServletPage.logout();
+        }
+    }
+
+    @Test
     public void salesPostPassiveTest() {
         salesPostPassiveServletPage.navigateTo();
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java
index f6e575f..71596af 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java
@@ -38,6 +38,8 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.*;
 
 public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
 
+    protected IdentityProviderResource identityProviderResource;
+
     @Before
     public void createUser() {
         log.debug("creating user for realm " + bc.providerRealmName());
@@ -59,7 +61,8 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
         log.debug("adding identity provider to realm " + bc.consumerRealmName());
 
         RealmResource realm = adminClient.realm(bc.consumerRealmName());
-        realm.identityProviders().create(bc.setUpIdentityProvider(suiteContext));
+        realm.identityProviders().create(bc.setUpIdentityProvider(suiteContext)).close();
+        identityProviderResource = realm.identityProviders().get(bc.getIDPAlias());
     }
 
     @Before
@@ -70,7 +73,7 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
             for (ClientRepresentation client : clients) {
                 log.debug("adding client " + client.getName() + " to realm " + bc.providerRealmName());
 
-                providerRealm.clients().create(client);
+                providerRealm.clients().create(client).close();
             }
         }
 
@@ -80,7 +83,7 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
             for (ClientRepresentation client : clients) {
                 log.debug("adding client " + client.getName() + " to realm " + bc.consumerRealmName());
 
-                consumerRealm.clients().create(client);
+                consumerRealm.clients().create(client).close();
             }
         }
     }
@@ -88,6 +91,12 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
 
     @Test
     public void testLogInAsUserInIDP() {
+        loginUser();
+
+        testSingleLogout();
+    }
+
+    protected void loginUser() {
         driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
 
         log.debug("Clicking social " + bc.getIDPAlias());
@@ -96,16 +105,16 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
         waitForPage(driver, "log in to");
 
         Assert.assertTrue("Driver should be on the provider realm page right now",
-                driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
+          driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
 
         log.debug("Logging in");
         accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
 
         waitForPage(driver, "update account information");
 
-        Assert.assertTrue(updateAccountInformationPage.isCurrent());
+        updateAccountInformationPage.assertCurrent();
         Assert.assertTrue("We must be on correct realm right now",
-                driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
+          driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
 
         log.debug("Updating info on updateAccount page");
         updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname");
@@ -126,9 +135,7 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
         }
 
         Assert.assertTrue("There must be user " + bc.getUserLogin() + " in realm " + bc.consumerRealmName(),
-                isUserFound);
-
-        testSingleLogout();
+          isUserFound);
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
index e5f5b8d..da0bc2b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
@@ -6,6 +6,7 @@
 package org.keycloak.testsuite.broker;
 
 import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.saml.SamlConfigAttributes;
 import org.keycloak.protocol.saml.SamlProtocol;
 import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
 import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper;
@@ -22,6 +23,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.*;
 import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
 import static org.keycloak.testsuite.broker.BrokerTestTools.*;
 
@@ -63,17 +65,17 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
 
         Map<String, String> attributes = new HashMap<>();
 
-        attributes.put("saml.authnstatement", "true");
-        attributes.put("saml_single_logout_service_url_post",
+        attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, "true");
+        attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE,
                 getAuthRoot(suiteContext) + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint");
-        attributes.put("saml_assertion_consumer_url_post",
+        attributes.put(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE,
                 getAuthRoot(suiteContext) + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint");
-        attributes.put("saml_force_name_id_format", "true");
-        attributes.put("saml_name_id_format", "username");
-        attributes.put("saml.assertion.signature", "false");
-        attributes.put("saml.server.signature", "false");
-        attributes.put("saml.client.signature", "false");
-        attributes.put("saml.encrypt", "false");
+        attributes.put(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, "true");
+        attributes.put(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, "username");
+        attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "false");
+        attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false");
+        attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false");
+        attributes.put(SamlConfigAttributes.SAML_ENCRYPT, "false");
 
         client.setAttributes(attributes);
 
@@ -133,15 +135,15 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
 
         Map<String, String> config = idp.getConfig();
 
-        config.put("singleSignOnServiceUrl", getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
-        config.put("singleLogoutServiceUrl", getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
-        config.put("nameIDPolicyFormat", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
-        config.put("forceAuthn", "true");
-        config.put("postBindingResponse", "true");
-        config.put("postBindingAuthnRequest", "true");
-        config.put("validateSignature", "false");
-        config.put("wantAuthnRequestsSigned", "false");
-        config.put("backchannelSupported", "true");
+        config.put(SINGLE_SIGN_ON_SERVICE_URL, getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
+        config.put(SINGLE_LOGOUT_SERVICE_URL, getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
+        config.put(NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
+        config.put(FORCE_AUTHN, "true");
+        config.put(POST_BINDING_RESPONSE, "true");
+        config.put(POST_BINDING_AUTHN_REQUEST, "true");
+        config.put(VALIDATE_SIGNATURE, "false");
+        config.put(WANT_AUTHN_REQUESTS_SIGNED, "false");
+        config.put(BACKCHANNEL_SUPPORTED, "true");
 
         return idp;
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java
index b4825cd..ef23a9a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java
@@ -1,19 +1,29 @@
 package org.keycloak.testsuite.broker;
 
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
+import org.keycloak.protocol.saml.SamlConfigAttributes;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.arquillian.SuiteContext;
 
+import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
+import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater;
+import java.io.Closeable;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
 import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
+import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl;
 
 public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
 
-    public static class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration {
+    public class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration {
 
         @Override
         public RealmRepresentation createProviderRealm() {
@@ -39,6 +49,9 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
         public List<ClientRepresentation> createProviderClients(SuiteContext suiteContext) {
             List<ClientRepresentation> clientRepresentationList = super.createProviderClients(suiteContext);
 
+            String consumerCert = adminClient.realm(consumerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
+            Assert.assertThat(consumerCert, Matchers.notNullValue());
+
             for (ClientRepresentation client : clientRepresentationList) {
                 client.setClientAuthenticatorType("client-secret");
                 client.setSurrogateAuthRequired(false);
@@ -49,12 +62,11 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
                     client.setAttributes(attributes);
                 }
 
-                attributes.put("saml.assertion.signature", "true");
-                attributes.put("saml.server.signature", "true");
-                attributes.put("saml.client.signature", "true");
-                attributes.put("saml.signature.algorithm", "RSA_SHA256");
-                attributes.put("saml.signing.private.key", IDP_SAML_SIGN_KEY);
-                attributes.put("saml.signing.certificate", IDP_SAML_SIGN_CERT);
+                attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "true");
+                attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "true");
+                attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "true");
+                attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, "RSA_SHA256");
+                attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, consumerCert);
             }
 
             return clientRepresentationList;
@@ -64,11 +76,15 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
         public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
             IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext);
 
+            String providerCert = adminClient.realm(providerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
+            Assert.assertThat(providerCert, Matchers.notNullValue());
+
             Map<String, String> config = result.getConfig();
 
-            config.put("validateSignature", "true");
-            config.put("wantAuthnRequestsSigned", "true");
-            config.put("signingCertificate", IDP_SAML_SIGN_CERT);
+            config.put(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE, "true");
+            config.put(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, "true");
+            config.put(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "true");
+            config.put(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, providerCert);
 
             return result;
         }
@@ -76,7 +92,50 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
 
     @Override
     protected BrokerConfiguration getBrokerConfiguration() {
-        return KcSamlSignedBrokerConfiguration.INSTANCE;
+        return new KcSamlSignedBrokerConfiguration();
     }
 
+    @Test
+    public void testSignedEncryptedAssertions() throws Exception {
+        ClientRepresentation client = adminClient.realm(bc.providerRealmName())
+          .clients()
+          .findByClientId(bc.getIDPClientIdInProviderRealm(suiteContext))
+          .get(0);
+
+        final ClientResource clientResource = realmsResouce().realm(bc.providerRealmName()).clients().get(client.getId());
+        Assert.assertThat(clientResource, Matchers.notNullValue());
+
+        String providerCert = adminClient.realm(bc.providerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
+        Assert.assertThat(providerCert, Matchers.notNullValue());
+
+        String consumerCert = adminClient.realm(bc.consumerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
+        Assert.assertThat(consumerCert, Matchers.notNullValue());
+
+        try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
+            .setAttribute(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE, "true")
+            .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, "true")
+            .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_ENCRYPTED, "true")
+            .setAttribute(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "false")
+            .setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, providerCert)
+            .update();
+          Closeable clientUpdater = new ClientAttributeUpdater(clientResource)
+            .setAttribute(SamlConfigAttributes.SAML_ENCRYPT, "true")
+            .setAttribute(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, consumerCert)
+            .setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false")      // only sign assertions
+            .setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "true")
+            .setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false")
+            .update())
+        {
+            // Login should pass because assertion is signed.
+            loginUser();
+
+            // Logout should fail because logout response is not signed.
+            driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
+                    + "/auth/realms/" + bc.providerRealmName()
+                    + "/protocol/" + "openid-connect"
+                    + "/logout?redirect_uri=" + encodeUrl(getAccountUrl(bc.providerRealmName())));
+
+            errorPage.assertCurrent();
+        }
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java
new file mode 100644
index 0000000..5f9fd0e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.broker;
+
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.arquillian.SuiteContext;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
+
+public class KcSamlSignedDocumentOnlyBrokerTest extends KcSamlBrokerTest {
+
+    public static class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration {
+
+        @Override
+        public RealmRepresentation createProviderRealm() {
+            RealmRepresentation realm = super.createProviderRealm();
+
+            realm.setPublicKey(REALM_PUBLIC_KEY);
+            realm.setPrivateKey(REALM_PRIVATE_KEY);
+
+            return realm;
+        }
+
+        @Override
+        public RealmRepresentation createConsumerRealm() {
+            RealmRepresentation realm = super.createConsumerRealm();
+
+            realm.setPublicKey(REALM_PUBLIC_KEY);
+            realm.setPrivateKey(REALM_PRIVATE_KEY);
+
+            return realm;
+        }
+
+        @Override
+        public List<ClientRepresentation> createProviderClients(SuiteContext suiteContext) {
+            List<ClientRepresentation> clientRepresentationList = super.createProviderClients(suiteContext);
+
+            for (ClientRepresentation client : clientRepresentationList) {
+                client.setClientAuthenticatorType("client-secret");
+                client.setSurrogateAuthRequired(false);
+
+                Map<String, String> attributes = client.getAttributes();
+                if (attributes == null) {
+                    attributes = new HashMap<>();
+                    client.setAttributes(attributes);
+                }
+
+                attributes.put("saml.assertion.signature", "false");
+                attributes.put("saml.server.signature", "true");
+                attributes.put("saml.client.signature", "true");
+                attributes.put("saml.signature.algorithm", "RSA_SHA256");
+                attributes.put("saml.signing.private.key", IDP_SAML_SIGN_KEY);
+                attributes.put("saml.signing.certificate", IDP_SAML_SIGN_CERT);
+            }
+
+            return clientRepresentationList;
+        }
+
+        @Override
+        public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
+            IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext);
+
+            Map<String, String> config = result.getConfig();
+
+            config.put("validateSignature", "true");
+            config.put("wantAssertionsSigned", "false");
+            config.put("wantAuthnRequestsSigned", "true");
+            config.put("signingCertificate", IDP_SAML_SIGN_CERT);
+
+            return result;
+        }
+    }
+
+    @Override
+    protected BrokerConfiguration getBrokerConfiguration() {
+        return KcSamlSignedBrokerConfiguration.INSTANCE;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-enc-sign-assertions-only/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-enc-sign-assertions-only/WEB-INF/keycloak-saml.xml
new file mode 100644
index 0000000..39df7d9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-enc-sign-assertions-only/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,65 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
+                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                       xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
+    <SP entityID="http://localhost:8081/sales-post-enc-sign-assertions-only/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" encryption="true">
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
+                    <Certificate alias="http://localhost:8080/sales-post-enc/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService signRequest="true"
+                                 validateResponseSignature="false"
+                                 validateAssertionSignature="true"
+                                 requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    validateRequestSignature="true"
+                    validateResponseSignature="false"
+                    signRequest="true"
+                    signResponse="true"
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true" >
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-enc-sign-assertions-only/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-enc-sign-assertions-only/WEB-INF/keystore.jks
new file mode 100644
index 0000000..822162c
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-enc-sign-assertions-only/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
index bb0e0d1..2604eb7 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
@@ -317,6 +317,25 @@
             }
         },
         {
+            "clientId": "http://localhost:8081/sales-post-enc-sign-assertions-only/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/sales-post-enc-sign-assertions-only",
+            "redirectUris": [
+            ],
+            "attributes": {
+                "saml.server.signature": "false",
+                "saml.assertion.signature": "true",
+                "saml.signature.algorithm": "RSA_SHA512",
+                "saml.client.signature": "true",
+                "saml.encrypt": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
+                "saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+            }
+        },
+        {
             "clientId": "http://localhost:8081/employee-sig/",
             "enabled": true,
             "protocol": "saml",