[java-opensaml] branch maint-5.1 updated: OSJ-427: Simple signature verification fails to detect parameter ...

Brent Putman putmanb at georgetown.edu
Sat Mar 22 02:59:26 UTC 2025


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

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

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

The following commit(s) were added to refs/heads/maint-5.1 by this push:
     new d2bbf48f9 OSJ-427: Simple signature verification fails to detect parameter ...
d2bbf48f9 is described below

commit d2bbf48f902d1e9e5ec121374c9da303da07ea44
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Fri Mar 21 20:22:25 2025 -0400

    OSJ-427: Simple signature verification fails to detect parameter ...
    
    Simple signature decoders now build and store the content byte[] over
    which signature is evaluated, consistent with what the decoder "saw"
    as the SAML message.
---
 .../BaseSAMLSimpleSignatureSecurityHandler.java    |  11 +-
 .../decoding/impl/HTTPPostSimpleSignDecoder.java   |  85 +++++++++++
 .../decoding/impl/HTTPRedirectDeflateDecoder.java  | 169 ++++++++++++++++++++-
 .../decoding/impl/SimpleSignatureContext.java      |  47 ++++++
 .../SAML2HTTPPostSimpleSignSecurityHandler.java    |  57 +------
 ...TTPRedirectDeflateSignatureSecurityHandler.java |  95 +-----------
 .../impl/HTTPPostSimpleSignDecoderTest.java        | 157 +++++++++++++++++++
 .../impl/HTTPRedirectDeflateDecoderTest.java       |  47 ++++++
 ...SAML2HTTPPostSimpleSignSecurityHandlerTest.java |  40 +++--
 ...edirectDeflateSignatureSecurityHandlerTest.java |  33 +++-
 10 files changed, 582 insertions(+), 159 deletions(-)

diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/BaseSAMLSimpleSignatureSecurityHandler.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/BaseSAMLSimpleSignatureSecurityHandler.java
index 9d50668c6..f686c62ef 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/BaseSAMLSimpleSignatureSecurityHandler.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/BaseSAMLSimpleSignatureSecurityHandler.java
@@ -28,6 +28,7 @@ import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
 import org.opensaml.saml.common.messaging.context.SAMLProtocolContext;
 import org.opensaml.saml.criterion.EntityRoleCriterion;
 import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.saml.saml2.binding.decoding.impl.SimpleSignatureContext;
 import org.opensaml.security.SecurityException;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.credential.UsageType;
@@ -187,7 +188,7 @@ public abstract class BaseSAMLSimpleSignatureSecurityHandler extends AbstractMes
         }
         assert sigAlg != null;
 
-        final byte[] signedContent = getSignedContent();
+        final byte[] signedContent = getSignedContent(messageContext);
         if (signedContent == null || signedContent.length == 0) {
             log.warn("{} Signed content could not be extracted from HTTP request, cannot validate", getLogPrefix());
             return;
@@ -415,10 +416,16 @@ public abstract class BaseSAMLSimpleSignatureSecurityHandler extends AbstractMes
      * Get the content over which to validate the signature, in the form suitable for input into
      * {@link SignatureTrustEngine#validate(byte[], byte[], String, CriteriaSet, Credential)}.
      * 
+     * @param messageContext the message context which is being evaluated
+     * 
      * @return the signed content extracted from the request, in the format suitable for input to the trust engine.
      * @throws MessageHandlerException thrown if there is an error during request processing
      */
-    @Nullable protected abstract byte[] getSignedContent() throws MessageHandlerException;
+    @Nullable protected byte[] getSignedContent(@Nonnull final MessageContext messageContext)
+            throws MessageHandlerException {
+       
+        return messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent();
+    };
 
     /**
      * Determine whether the rule should handle the request, based on the unwrapped HTTP servlet request and/or message
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoder.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoder.java
index 28f5b2a64..fc61db6cc 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoder.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoder.java
@@ -14,19 +14,31 @@
 
 package org.opensaml.saml.saml2.binding.decoding.impl;
 
+import java.io.UnsupportedEncodingException;
+
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.decoder.MessageDecodingException;
 import org.opensaml.saml.common.binding.SAMLBindingSupport;
 import org.opensaml.saml.common.messaging.context.SAMLBindingContext;
 import org.opensaml.saml.common.xml.SAMLConstants;
+import org.slf4j.Logger;
 
 import com.google.common.base.Strings;
 
+import jakarta.servlet.http.HttpServletRequest;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.DecodingException;
+import net.shibboleth.shared.primitive.LoggerFactory;
 
 /** Message decoder implementing the SAML 2.0 HTTP POST-SimpleSign binding. */
 public class HTTPPostSimpleSignDecoder extends HTTPPostDecoder {
+    
+    /** Logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(HTTPPostSimpleSignDecoder.class);
 
     /** {@inheritDoc} */
     @Nonnull @NotEmpty public String getBindingURI() {
@@ -47,4 +59,77 @@ public class HTTPPostSimpleSignDecoder extends HTTPPostDecoder {
         bindingContext.setIntendedDestinationEndpointURIRequired(SAMLBindingSupport.isMessageSigned(messageContext));
     }
     
+    /** {@inheritDoc} */
+    @Override
+    protected void doDecode() throws MessageDecodingException {
+        super.doDecode();
+        
+        final byte[] signedContent = getSignedContent();
+        if (signedContent == null) {
+            log.warn("Failed to build signed content data, signature evaluation will be skipped");
+            return;
+        }
+
+        getMessageContext().ensureSubcontext(SimpleSignatureContext.class).setSignedContent(signedContent);
+    }
+
+    /**
+     * Get the signed content data.
+     * 
+     * @return the signed content
+     * 
+     * @throws MessageDecodingException if there is a fatal issue building the signed content
+     */
+    @Nullable protected byte[] getSignedContent() throws MessageDecodingException {
+        final HttpServletRequest request = getHttpServletRequest();
+        
+        final StringBuilder builder = new StringBuilder();
+        final String samlMsg;
+        try {
+            if (request.getParameter("SAMLRequest") != null) {
+                samlMsg = new String(Base64Support.decode(request.getParameter("SAMLRequest")), "UTF-8");
+                builder.append("SAMLRequest=" + samlMsg);
+            } else if (request.getParameter("SAMLResponse") != null) {
+                samlMsg = new String(Base64Support.decode(request.getParameter("SAMLResponse")), "UTF-8");
+                builder.append("SAMLResponse=" + samlMsg);
+            } else {
+                log.warn("Could not extract either a SAMLRequest or a SAMLResponse from the form control data");
+                return null;
+            }
+        } catch (final UnsupportedEncodingException e) {
+            log.error("UTF-8 encoding is not supported, this VM is not Java compliant");
+            throw new MessageDecodingException("Unable to process message, UTF-8 encoding is not supported");
+        } catch (final DecodingException e) {
+            log.error("Unable to Base64 decode either a SAMLRequest or a SAMLResponse from the form control data");
+            throw new MessageDecodingException("Unable to Base64 decode either a SAMLRequest or a SAMLResponse "
+                    + "from the form control data",e);
+        }
+
+        // Optional
+        if (request.getParameter("RelayState") != null) {
+            builder.append("&RelayState=" + request.getParameter("RelayState"));
+        }
+
+        // Mandatory
+        if (request.getParameter("SigAlg") == null) {
+            log.warn("Signature algorithm could not be extracted from request, cannot build simple signature content");
+            return null;
+        }
+        builder.append("&SigAlg=" + request.getParameter("SigAlg"));
+
+        final String constructed = builder.toString();
+        if (Strings.isNullOrEmpty(constructed)) {
+            log.warn("Could not construct signed content string from form control data");
+            return null;
+        }
+        log.debug("Constructed signed content string for HTTP-Post-SimpleSign {}", constructed);
+
+        try {
+            return constructed.getBytes("UTF-8");
+        } catch (final UnsupportedEncodingException e) {
+            log.error("UTF-8 encoding is not supported, this VM is not Java compliant");
+            throw new MessageDecodingException("Unable to process message, UTF-8 encoding is not supported");
+        }
+    }
+    
 }
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoder.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoder.java
index f41847b54..c30baa982 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoder.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoder.java
@@ -17,6 +17,10 @@ package org.opensaml.saml.saml2.binding.decoding.impl;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
 
@@ -39,6 +43,9 @@ import com.google.common.base.Strings;
 import jakarta.servlet.http.HttpServletRequest;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.net.URISupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
 
@@ -96,8 +103,15 @@ public class HTTPRedirectDeflateDecoder extends BaseSAMLHttpServletRequestDecode
         log.debug("Decoded RelayState: {}", relayState);
         SAMLBindingSupport.setRelayState(messageContext, relayState);
 
-        final String samlMessageEncoded = !Strings.isNullOrEmpty(request.getParameter("SAMLRequest"))
-                ? request.getParameter("SAMLRequest") : request.getParameter("SAMLResponse");
+        String samlMessageEncoded = null;
+        String samlMessageParamName = null;
+        if (!Strings.isNullOrEmpty(request.getParameter("SAMLRequest"))) {
+            samlMessageParamName = "SAMLRequest";
+            samlMessageEncoded = request.getParameter("SAMLRequest");
+        } else if (!Strings.isNullOrEmpty(request.getParameter("SAMLResponse"))) {
+            samlMessageParamName = "SAMLResponse";
+            samlMessageEncoded = request.getParameter("SAMLResponse");
+        }
 
         if (samlMessageEncoded != null) {
             try (final InputStream samlMessageIns = decodeMessage(samlMessageEncoded)) {
@@ -111,12 +125,163 @@ public class HTTPRedirectDeflateDecoder extends BaseSAMLHttpServletRequestDecode
             throw new MessageDecodingException(
                     "No SAMLRequest or SAMLResponse query path parameter, invalid SAML 2 HTTP Redirect message");
         }
+        
+        populateSimpleSignatureContext(messageContext, samlMessageParamName, samlMessageEncoded);
 
         populateBindingContext(messageContext);
 
         setMessageContext(messageContext);
     }
 
+    /**
+     * Build signed content string and populate the {@link SimpleSignatureContext}.
+     * 
+     * @param messageContext the current message context
+     * @param samlMessageParamName the URL-decoded SAML message parameter name
+     * @param samlMessage the URL-decoded Base64-encoded SAML message data
+     * 
+     * @throws MessageDecodingException if there is a fatal issue building the signed content
+     */
+    protected void populateSimpleSignatureContext(@Nonnull final MessageContext messageContext,
+            @Nonnull final String samlMessageParamName, @Nonnull final String samlMessage)
+                    throws MessageDecodingException {
+
+        messageContext.ensureSubcontext(SimpleSignatureContext.class).setSignedContent(
+                getSignedContent(samlMessageParamName, samlMessage));
+    }
+    
+    /**
+     * Get the signed content data.
+     * 
+     * @param samlMessageParamName the URL-decoded SAML message parameter name
+     * @param samlMessage the URL-decoded Base64-encoded SAML message data
+     * 
+     * @return the signed content
+     * 
+     * @throws MessageDecodingException if there is a fatal issue building the signed content
+     */
+    @Nullable private byte[] getSignedContent(@Nonnull final String samlMessageParamName,
+            @Nonnull final String samlMessage) throws MessageDecodingException {
+
+        // We need the raw non-URL-decoded query string param values for HTTP-Redirect DEFLATE simple signature
+        // validation.
+        // We have to construct a string containing the signature input by accessing the
+        // request directly. We can't use the decoded parameters because we need the raw
+        // data and URL-encoding isn't canonical.
+        final String queryString = getHttpServletRequest().getQueryString();
+        log.debug("Constructing signed content string from URL query string {}", queryString);
+
+        final String constructed = buildSignedContentString(queryString, samlMessageParamName, samlMessage);
+        if (Strings.isNullOrEmpty(constructed)) {
+            log.warn("Could not extract signed content string from query string");
+            return null;
+        }
+        log.debug("Constructed signed content string for HTTP-Redirect DEFLATE {}", constructed);
+
+        try {
+            return constructed.getBytes("UTF-8");
+        } catch (final UnsupportedEncodingException e) {
+            log.error("UTF-8 encoding is not supported, this VM is not Java compliant");
+            throw new MessageDecodingException("Unable to process message, UTF-8 encoding is not supported");
+        }
+    }
+
+    /**
+     * Extract the raw request parameters and build a string representation of the content that was signed.
+     * 
+     * @param queryString the raw HTTP query string from the request
+     * @param samlMessageParamName the URL-decoded SAML message parameter name
+     * @param samlMessage the URL-decoded Base64-encoded SAML message data
+     * 
+     * @return a string representation of the signed content
+     * 
+     * @throws MessageDecodingException thrown if there is an error during request processing
+     */
+    @Nonnull @NotEmpty private String buildSignedContentString(@Nullable final String queryString,
+            @Nonnull final String samlMessageParamName, @Nonnull final String samlMessage)
+                    throws MessageDecodingException {
+
+        final StringBuilder builder = new StringBuilder();
+
+        if (!appendSAMLMessageParameter(builder, queryString, samlMessageParamName, samlMessage)) {
+            log.warn("Could not extract SAML message '{}' from the query string, cannot build simple signature content",
+                    samlMessageParamName);
+            return null;
+        }
+
+        // This is optional
+        appendParameter(builder, queryString, "RelayState");
+
+        // This is mandatory
+        if (!appendParameter(builder, queryString, "SigAlg")) {
+            log.warn("Signature algorithm could not be extracted from request, cannot build simple signature content");
+            return null;
+        }
+
+        return builder.toString();
+    }
+    
+    /**
+     * Find the raw query string parameter indicated and append it to the string builder.
+     * 
+     * The appended value will be in the form 'paramName=paramValue' (minus the quotes).
+     * 
+     * @param builder string builder to which to append the parameter
+     * @param queryString the URL query string containing parameters
+     * @param paramName the name of the SAML message parameter to append
+     * @param paramValue the value of the SAML message parameter to append
+     * @return true if parameter was found, false otherwise
+     */
+    private boolean appendSAMLMessageParameter(@Nonnull final StringBuilder builder, @Nullable final String queryString,
+            @Nonnull final String paramName, @Nonnull final String paramValue) {
+
+        final List<Pair<String,String>> rawParams = URISupport.getRawQueryStringParameters(queryString, paramName)
+                .stream()
+                .filter(p -> Objects.equals(paramValue, URISupport.doURLDecode(p.getSecond())))
+                .collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
+
+        if (rawParams.isEmpty() || rawParams.size() > 1) {
+            log.debug("SAML message raw params extraction resulted in an invalid # of params: {}", rawParams.size());
+            return false;
+        }
+        
+        final Pair<String,String> rawParam = rawParams.get(0);
+
+        if (builder.length() > 0) {
+            builder.append('&');
+        }
+
+        builder.append(rawParam.getFirst() + "=" + rawParam.getSecond());
+
+        return true;
+    }
+
+    /**
+     * Find the raw query string parameter indicated and append it to the string builder.
+     * 
+     * The appended value will be in the form 'paramName=paramValue' (minus the quotes).
+     * 
+     * @param builder string builder to which to append the parameter
+     * @param queryString the URL query string containing parameters
+     * @param paramName the name of the parameter to append
+     * @return true if parameter was found, false otherwise
+     */
+    private boolean appendParameter(@Nonnull final StringBuilder builder, @Nullable final String queryString,
+            @Nullable final String paramName) {
+        final String rawParam = URISupport.getRawQueryStringParameter(queryString, paramName);
+        if (rawParam == null) {
+            return false;
+        }
+
+        if (builder.length() > 0) {
+            builder.append('&');
+        }
+
+        builder.append(rawParam);
+
+        return true;
+    }
+
     /**
      * Base64 decodes the SAML message and then decompresses the message.
      * 
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/SimpleSignatureContext.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/SimpleSignatureContext.java
new file mode 100644
index 000000000..07b5b8c7b
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/decoding/impl/SimpleSignatureContext.java
@@ -0,0 +1,47 @@
+/*
+ * 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.opensaml.saml.saml2.binding.decoding.impl;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+
+/**
+ * Context implementation holding data related to SAML 2 "simple signature" evaluation.
+ */
+public class SimpleSignatureContext extends BaseContext {
+    
+    /** The signed content over which signature evaluation will be performed. */
+    @Nullable private byte[] signedContent;
+    
+    /**
+     * Get the signed content over which signature evaluation will be performed.
+     * 
+     * @return the signed content
+     */
+    @Nullable public byte[] getSignedContent() {
+        return signedContent;
+    }
+    
+    /**
+     * Set the signed content over which signature evaluation will be performed.
+     * 
+     * @param content the signed content
+     */
+    public void setSignedContent(@Nullable final byte[] content) {
+        signedContent = content;
+    }
+
+}
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandler.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandler.java
index e3d4fe6aa..1b38b75e0 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandler.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandler.java
@@ -15,12 +15,10 @@
 package org.opensaml.saml.saml2.binding.security.impl;
 
 import java.io.ByteArrayInputStream;
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.List;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.io.Unmarshaller;
@@ -28,6 +26,8 @@ import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.handler.MessageHandlerException;
 import org.opensaml.saml.common.binding.security.impl.BaseSAMLSimpleSignatureSecurityHandler;
+import org.opensaml.saml.common.messaging.context.SAMLBindingContext;
+import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
 import org.opensaml.xmlsec.keyinfo.KeyInfoCriterion;
@@ -37,7 +37,6 @@ import org.w3c.dom.Document;
 
 import com.google.common.base.Strings;
 
-import jakarta.servlet.http.HttpServletRequest;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NotLive;
 import net.shibboleth.shared.annotation.constraint.Unmodifiable;
@@ -116,55 +115,9 @@ public class SAML2HTTPPostSimpleSignSecurityHandler extends BaseSAMLSimpleSignat
     /** {@inheritDoc} */
     @Override
     protected boolean ruleHandles(@Nonnull final MessageContext messageContext) {
-        return "POST".equals(getHttpServletRequest().getMethod());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable protected byte[] getSignedContent() throws MessageHandlerException {
-        final HttpServletRequest request = getHttpServletRequest();
-        
-        final StringBuilder builder = new StringBuilder();
-        final String samlMsg;
-        try {
-            if (request.getParameter("SAMLRequest") != null) {
-                samlMsg = new String(Base64Support.decode(request.getParameter("SAMLRequest")), "UTF-8");
-                builder.append("SAMLRequest=" + samlMsg);
-            } else if (request.getParameter("SAMLResponse") != null) {
-                samlMsg = new String(Base64Support.decode(request.getParameter("SAMLResponse")), "UTF-8");
-                builder.append("SAMLResponse=" + samlMsg);
-            } else {
-                log.warn("Could not extract either a SAMLRequest or a SAMLResponse from the form control data");
-                throw new MessageHandlerException("Extract of SAMLRequest or SAMLResponse from form control data");
-            }
-        } catch (final UnsupportedEncodingException e) {
-            log.error("UTF-8 encoding is not supported, this VM is not Java compliant");
-            throw new MessageHandlerException("Unable to process message, UTF-8 encoding is not supported");
-        } catch (final DecodingException e) {
-            log.error("Unable to Base64 decode either a SAMLRequest or a SAMLResponse from the form control data");
-            throw new MessageHandlerException("Unable to Base64 decode either a SAMLRequest or a SAMLResponse "
-                    + "from the form control data",e);
-        }
-
-        if (request.getParameter("RelayState") != null) {
-            builder.append("&RelayState=" + request.getParameter("RelayState"));
-        }
-
-        builder.append("&SigAlg=" + request.getParameter("SigAlg"));
-
-        final String constructed = builder.toString();
-        if (Strings.isNullOrEmpty(constructed)) {
-            log.warn("Could not construct signed content string from form control data");
-            return null;
-        }
-        log.debug("Constructed signed content string for HTTP-Post-SimpleSign {}", constructed);
-
-        try {
-            return constructed.getBytes("UTF-8");
-        } catch (final UnsupportedEncodingException e) {
-            log.error("UTF-8 encoding is not supported, this VM is not Java compliant");
-            throw new MessageHandlerException("Unable to process message, UTF-8 encoding is not supported");
-        }
+        return "POST".equals(getHttpServletRequest().getMethod())
+                && SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI.equals(
+                        messageContext.ensureSubcontext(SAMLBindingContext.class).getBindingUri());
     }
 
     /** {@inheritDoc} */
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandler.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandler.java
index 5f338068e..6af9ad9e3 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandler.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandler.java
@@ -14,21 +14,16 @@
 
 package org.opensaml.saml.saml2.binding.security.impl;
 
-import java.io.UnsupportedEncodingException;
-
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.net.URISupport;
-import net.shibboleth.shared.primitive.LoggerFactory;
 
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.handler.MessageHandlerException;
 import org.opensaml.saml.common.binding.security.impl.BaseSAMLSimpleSignatureSecurityHandler;
+import org.opensaml.saml.common.messaging.context.SAMLBindingContext;
+import org.opensaml.saml.common.xml.SAMLConstants;
 import org.slf4j.Logger;
 
-import com.google.common.base.Strings;
+import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
  * Message handler which evaluates simple "blob" signatures according to the SAML 2 HTTP-Redirect DEFLATE binding.
@@ -40,86 +35,10 @@ public class SAML2HTTPRedirectDeflateSignatureSecurityHandler extends BaseSAMLSi
 
     /** {@inheritDoc} */
     @Override
-    protected boolean ruleHandles(@Nonnull final MessageContext messgaeContext) throws MessageHandlerException {
-        return "GET".equals(getHttpServletRequest().getMethod());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable protected byte[] getSignedContent() throws MessageHandlerException {
-        // We need the raw non-URL-decoded query string param values for HTTP-Redirect DEFLATE simple signature
-        // validation.
-        // We have to construct a string containing the signature input by accessing the
-        // request directly. We can't use the decoded parameters because we need the raw
-        // data and URL-encoding isn't canonical.
-        final String queryString = getHttpServletRequest().getQueryString();
-        log.debug("Constructing signed content string from URL query string {}", queryString);
-
-        final String constructed = buildSignedContentString(queryString);
-        if (Strings.isNullOrEmpty(constructed)) {
-            log.warn("Could not extract signed content string from query string");
-            return null;
-        }
-        log.debug("Constructed signed content string for HTTP-Redirect DEFLATE {}", constructed);
-
-        try {
-            return constructed.getBytes("UTF-8");
-        } catch (final UnsupportedEncodingException e) {
-            log.error("UTF-8 encoding is not supported, this VM is not Java compliant");
-            throw new MessageHandlerException("Unable to process message, UTF-8 encoding is not supported");
-        }
-    }
-
-    /**
-     * Extract the raw request parameters and build a string representation of the content that was signed.
-     * 
-     * @param queryString the raw HTTP query string from the request
-     * @return a string representation of the signed content
-     * @throws MessageHandlerException thrown if there is an error during request processing
-     */
-    @Nonnull @NotEmpty private String buildSignedContentString(@Nullable final String queryString)
-            throws MessageHandlerException {
-        final StringBuilder builder = new StringBuilder();
-
-        // One of these two is mandatory
-        if (!appendParameter(builder, queryString, "SAMLRequest")) {
-            if (!appendParameter(builder, queryString, "SAMLResponse")) {
-                log.warn("Could not extract either a SAMLRequest or a SAMLResponse from the query string");
-                throw new MessageHandlerException("Extract of SAMLRequest or SAMLResponse from query string failed");
-            }
-        }
-        // This is optional
-        appendParameter(builder, queryString, "RelayState");
-        // This is mandatory, but has already been checked in superclass
-        appendParameter(builder, queryString, "SigAlg");
-
-        return builder.toString();
-    }
-
-    /**
-     * Find the raw query string parameter indicated and append it to the string builder.
-     * 
-     * The appended value will be in the form 'paramName=paramValue' (minus the quotes).
-     * 
-     * @param builder string builder to which to append the parameter
-     * @param queryString the URL query string containing parameters
-     * @param paramName the name of the parameter to append
-     * @return true if parameter was found, false otherwise
-     */
-    private boolean appendParameter(@Nonnull final StringBuilder builder, @Nullable final String queryString,
-            @Nullable final String paramName) {
-        final String rawParam = URISupport.getRawQueryStringParameter(queryString, paramName);
-        if (rawParam == null) {
-            return false;
-        }
-
-        if (builder.length() > 0) {
-            builder.append('&');
-        }
-
-        builder.append(rawParam);
-
-        return true;
+    protected boolean ruleHandles(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+        return "GET".equals(getHttpServletRequest().getMethod())
+                && SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(
+                        messageContext.ensureSubcontext(SAMLBindingContext.class).getBindingUri());
     }
     
 }
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoderTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoderTest.java
new file mode 100644
index 000000000..d2218daf8
--- /dev/null
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPPostSimpleSignDecoderTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.opensaml.saml.saml2.binding.decoding.impl;
+
+import java.io.UnsupportedEncodingException;
+
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.decoder.MessageDecodingException;
+import org.opensaml.saml.common.binding.SAMLBindingSupport;
+import org.opensaml.saml.saml2.core.RequestAbstractType;
+import org.opensaml.saml.saml2.core.Response;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.DecodingException;
+import net.shibboleth.shared.net.URISupport;
+import net.shibboleth.shared.testing.ConstantSupplier;
+
+/**
+ * Test case for HTTP POST decoders.
+ */
+public class HTTPPostSimpleSignDecoderTest extends XMLObjectBaseTestCase {
+    
+    private String expectedRelayValue = "relay";
+    
+    private HTTPPostSimpleSignDecoder decoder;
+    
+    private MockHttpServletRequest httpRequest;
+    
+    /** Invalid base64 string as it has invalid trailing digits. */
+    private final static String INVALID_BASE64_TRAILING = "AB==";
+
+    @BeforeMethod
+    protected void setUp() throws Exception {
+        httpRequest = new MockHttpServletRequest();
+        httpRequest.setMethod("POST");
+        httpRequest.setParameter("RelayState", expectedRelayValue);
+        
+        decoder = new HTTPPostSimpleSignDecoder();
+        decoder.setParserPool(parserPool);
+        decoder.setHttpServletRequestSupplier(new ConstantSupplier<>(httpRequest));
+        decoder.initialize();
+    }
+    /**
+     * Test decoding a SAML httpRequest.
+     * 
+     * @throws MessageDecodingException ...
+     */
+    @Test
+    public void testRequestDecoding() throws MessageDecodingException {
+        httpRequest.setParameter("SAMLRequest", "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOkF1dGhuUm"
+                + "VxdWVzdCBJRD0iZm9vIiBJc3N1ZUluc3RhbnQ9IjE5NzAtMDEtMDFUMDA6MDA6MDAuMDAwWiIgVmVyc2lvbj0iMi4wIiB4bW"
+                + "xuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIi8+");
+        
+        decoder.decode();
+        final MessageContext messageContext = decoder.getMessageContext();
+        assert messageContext != null;
+
+        Assert.assertTrue(messageContext.getMessage() instanceof RequestAbstractType);
+        Assert.assertEquals(SAMLBindingSupport.getRelayState(messageContext), expectedRelayValue);
+        Assert.assertNull(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent());
+    }
+    
+ 
+    /**
+     * Test decoding a SAML httpRequest.
+     * 
+     * @throws MessageDecodingException ...
+     * @throws UnsupportedEncodingException ...
+     * @throws DecodingException  ...
+     */
+    @Test
+    public void testRequestDecodingWithSignature() throws MessageDecodingException, UnsupportedEncodingException, DecodingException {
+        httpRequest.setParameter("SAMLRequest", "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOkF1dGhuUm"
+                + "VxdWVzdCBJRD0iZm9vIiBJc3N1ZUluc3RhbnQ9IjE5NzAtMDEtMDFUMDA6MDA6MDAuMDAwWiIgVmVyc2lvbj0iMi4wIiB4bW"
+                + "xuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIi8+");
+        httpRequest.setParameter("SigAlg", "TheAlgorithm");
+        httpRequest.setParameter("Signature", "TheSignature");
+        // Note RelayState is already set to 'relay'
+        
+        decoder.decode();
+        final MessageContext messageContext = decoder.getMessageContext();
+        assert messageContext != null;
+
+        Assert.assertTrue(messageContext.getMessage() instanceof RequestAbstractType);
+        Assert.assertEquals(SAMLBindingSupport.getRelayState(messageContext), expectedRelayValue);
+        Assert.assertNotNull(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent());
+        
+        final byte[] expectedSignedContent = new StringBuilder()
+                .append("SAMLRequest=" + new String(Base64Support.decode(httpRequest.getParameter("SAMLRequest")), "UTF-8"))
+                .append("&")
+                .append("RelayState=" + httpRequest.getParameter("RelayState"))
+                .append("&")
+                .append("SigAlg=" + httpRequest.getParameter("SigAlg"))
+                .toString().getBytes("UTF-8");
+        //System.err.println("Actual:   " + new String(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent(), "UTF-8"));
+        //System.err.println("Expected: " + new String(expectedSignedContent, "UTF-8"));
+        Assert.assertEquals(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent(), expectedSignedContent);
+    }
+    
+    /**
+     * Test decoding a Base64 invalid SAML Request. Should throw a {@link MessageDecodingException} wrapping
+     * a {@link DecodingException}.
+     */
+    @Test
+    public void testInvalidRequestDecoding()  {
+        httpRequest.setParameter("SAMLRequest", INVALID_BASE64_TRAILING);        
+        try {
+            decoder.decode();
+        } catch (MessageDecodingException e) {
+            if(e.getCause() instanceof DecodingException){
+                //pass
+            } else {
+                Assert.fail("Expected DecodingException type");
+            }
+        }        
+    }
+
+    /**
+     * Test decoding a SAML response.
+     * 
+     * @throws MessageDecodingException ...
+     */
+    @Test
+    public void testResponseDecoding() throws MessageDecodingException {
+        httpRequest.setParameter("SAMLResponse", "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3Bvbn"
+                + "NlIElEPSJmb28iIElzc3VlSW5zdGFudD0iMTk3MC0wMS0wMVQwMDowMDowMC4wMDBaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnN"
+                + "hbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0Nv"
+                + "ZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjwvc"
+                + "2FtbHA6UmVzcG9uc2U+");
+
+        decoder.decode();
+        final MessageContext messageContext = decoder.getMessageContext();
+        assert messageContext != null;
+
+        Assert.assertTrue(messageContext.getMessage() instanceof Response);
+        Assert.assertEquals(SAMLBindingSupport.getRelayState(messageContext), expectedRelayValue);
+        Assert.assertNull(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent());
+    }
+    
+}
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoderTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoderTest.java
index d8f3abdc2..b02fbbc63 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoderTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/decoding/impl/HTTPRedirectDeflateDecoderTest.java
@@ -16,6 +16,7 @@ package org.opensaml.saml.saml2.binding.decoding.impl;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.zip.Deflater;
@@ -41,6 +42,9 @@ import org.w3c.dom.Element;
 
 import net.shibboleth.shared.codec.Base64Support;
 import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.net.URISupport;
 import net.shibboleth.shared.testing.ConstantSupplier;
 import net.shibboleth.shared.xml.SerializeSupport;
 
@@ -84,6 +88,7 @@ public class HTTPRedirectDeflateDecoderTest extends XMLObjectBaseTestCase {
 
         Assert.assertTrue(messageContext.getMessage() instanceof Response);
         Assert.assertEquals(SAMLBindingSupport.getRelayState(messageContext), expectedRelayValue);
+        Assert.assertNull(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent());
     }    
    
     @Test
@@ -102,6 +107,48 @@ public class HTTPRedirectDeflateDecoderTest extends XMLObjectBaseTestCase {
 
         Assert.assertTrue(messageContext.getMessage() instanceof RequestAbstractType);
         Assert.assertEquals(SAMLBindingSupport.getRelayState(messageContext), expectedRelayValue);
+        Assert.assertNull(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent());
+    }
+    
+    @Test
+    public void testRequestDecodingWithSignature() throws MessageDecodingException, MessageEncodingException, 
+                                                            MarshallingException, EncodingException, UnsupportedEncodingException {
+        final AuthnRequest samlRequest =
+                (AuthnRequest) unmarshallElement("/org/opensaml/saml/saml2/binding/AuthnRequest.xml");
+        assert samlRequest != null;
+        samlRequest.setDestination(null);
+
+        httpRequest.setParameter("SAMLRequest", encodeMessage(samlRequest));
+        httpRequest.setParameter("SigAlg", "TheAlgorithm");
+        httpRequest.setParameter("Signature", "TheSignature");
+        // Note RelayState is already set to 'relay'
+        
+        String query = URISupport.buildQuery(CollectionSupport.listOf(
+                new Pair<>("SAMLRequest", httpRequest.getParameter("SAMLRequest")),
+                new Pair<>("SigAlg", httpRequest.getParameter("SigAlg")),
+                new Pair<>("Signature", httpRequest.getParameter("Signature")),
+                new Pair<>("RelayState", httpRequest.getParameter("RelayState"))
+                ));
+        httpRequest.setQueryString(query);
+
+        decoder.decode();
+        final MessageContext messageContext = decoder.getMessageContext();
+        assert messageContext != null;
+
+        Assert.assertTrue(messageContext.getMessage() instanceof RequestAbstractType);
+        Assert.assertEquals(SAMLBindingSupport.getRelayState(messageContext), expectedRelayValue);
+        Assert.assertNotNull(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent());
+        
+        final byte[] expectedSignedContent = new StringBuilder()
+                .append(URISupport.getRawQueryStringParameter(httpRequest.getQueryString(), "SAMLRequest"))
+                .append("&")
+                .append(URISupport.getRawQueryStringParameter(httpRequest.getQueryString(), "RelayState"))
+                .append("&")
+                .append(URISupport.getRawQueryStringParameter(httpRequest.getQueryString(), "SigAlg"))
+                .toString().getBytes("UTF-8");
+        //System.err.println("Actual:   " + new String(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent(), "UTF-8"));
+        //System.err.println("Expected: " + new String(expectedSignedContent, "UTF-8"));
+        Assert.assertEquals(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent(), expectedSignedContent);
     }
     
     /**
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandlerTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandlerTest.java
index 5a8dfc83a..bcc631190 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandlerTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPPostSimpleSignSecurityHandlerTest.java
@@ -37,6 +37,8 @@ import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
 import org.opensaml.saml.common.messaging.context.SAMLProtocolContext;
 import org.opensaml.saml.common.testing.SAMLTestSupport;
 import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.binding.decoding.impl.HTTPPostSimpleSignDecoder;
+import org.opensaml.saml.saml2.binding.decoding.impl.SimpleSignatureContext;
 import org.opensaml.saml.saml2.binding.encoding.impl.HTTPPostSimpleSignEncoder;
 import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
@@ -217,20 +219,30 @@ public class SAML2HTTPPostSimpleSignSecurityHandlerTest extends XMLObjectBaseTes
         
         sigValParams = new SignatureValidationParameters();
         sigValParams.setSignatureTrustEngine(signatureTrustEngine);
-
-        handler = new SAML2HTTPPostSimpleSignSecurityHandler();
-        final HttpServletRequest httpRequest = buildServletRequest();
-        handler.setHttpServletRequestSupplier(new ConstantSupplier<>(httpRequest));
-        handler.setParser(parserPool);
-        handler.setKeyInfoResolver(kiResolver);
-        handler.initialize();
         
-        messageContext = new MessageContext();
+        final HttpServletRequest request = buildServletRequest();
+        
+        final HTTPPostSimpleSignDecoder decoder = new HTTPPostSimpleSignDecoder();
+        decoder.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
+        decoder.setParserPool(parserPool);
+        decoder.initialize();
+        
+        decoder.decode();
+
+        messageContext = decoder.getMessageContext();
+        assert messageContext != null;
         messageContext.setMessage(buildInboundSAMLMessage());
         messageContext.ensureSubcontext(SAMLPeerEntityContext.class).setEntityId(issuer);
         messageContext.ensureSubcontext(SAMLPeerEntityContext.class).setRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
         messageContext.ensureSubcontext(SAMLProtocolContext.class).setProtocol(SAMLConstants.SAML20P_NS);
         messageContext.ensureSubcontext(SecurityParametersContext.class).setSignatureValidationParameters(sigValParams);
+
+        handler = new SAML2HTTPPostSimpleSignSecurityHandler();
+        handler.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
+        handler.setParser(parserPool);
+        handler.setKeyInfoResolver(kiResolver);
+        handler.initialize();
+        
     }
 
     /**
@@ -279,14 +291,24 @@ public class SAML2HTTPPostSimpleSignSecurityHandlerTest extends XMLObjectBaseTes
      * Test context issuer set, invalid signature with trusted credential.
      * 
      * @throws MessageHandlerException ...
+     * @throws UnsupportedEncodingException ...
      */
     @Test(expectedExceptions=MessageHandlerException.class)
-    public void testInvalidSignature() throws MessageHandlerException {
+    public void testInvalidSignature() throws MessageHandlerException, UnsupportedEncodingException {
         trustedCredentials.add(signingX509Cred);
 
+        // Note: this is just for posterity and clarity as to what's going on. Can't manipulate the request anymore to cause signature failure,
+        // since the signed content is now obtained from the message context, as populated by the decoder.
         final MockHttpServletRequest request = (MockHttpServletRequest) handler.getHttpServletRequest();
         request.setParameter("RelayState", "AlteredData" + request.getParameter("RelayState"));
         
+        // This actually causes the expected signature failure
+        final String origSignedContent = new String(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent(), "UTF-8");
+        final String badSignedContent = origSignedContent.replaceFirst("RelayState=", "RelayState=AlteredData");
+        //System.err.println("Actual:   " + origSignedContent);
+        //System.err.println("Expected: " + badSignedContent);
+        messageContext.ensureSubcontext(SimpleSignatureContext.class).setSignedContent(badSignedContent.getBytes());
+        
         handler.invoke(messageContext);
     }
     
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandlerTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandlerTest.java
index 55e0fb3be..6cd2df936 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandlerTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/binding/security/impl/SAML2HTTPRedirectDeflateSignatureSecurityHandlerTest.java
@@ -14,6 +14,7 @@
 
 package org.opensaml.saml.saml2.binding.security.impl;
 
+import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.security.KeyException;
 import java.security.PrivateKey;
@@ -33,6 +34,8 @@ import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
 import org.opensaml.saml.common.messaging.context.SAMLProtocolContext;
 import org.opensaml.saml.common.testing.SAMLTestSupport;
 import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder;
+import org.opensaml.saml.saml2.binding.decoding.impl.SimpleSignatureContext;
 import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder;
 import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
@@ -199,17 +202,26 @@ public class SAML2HTTPRedirectDeflateSignatureSecurityHandlerTest extends XMLObj
         sigValParams = new SignatureValidationParameters();
         sigValParams.setSignatureTrustEngine(signatureTrustEngine);
         
-        handler = new SAML2HTTPRedirectDeflateSignatureSecurityHandler();
         final HttpServletRequest request = buildServletRequest();
-        handler.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
-        handler.initialize();
         
-        messageContext = new MessageContext();
+        final HTTPRedirectDeflateDecoder decoder = new HTTPRedirectDeflateDecoder();
+        decoder.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
+        decoder.setParserPool(parserPool);
+        decoder.initialize();
+        
+        decoder.decode();
+
+        messageContext = decoder.getMessageContext();
+        assert messageContext != null;
         messageContext.setMessage(buildInboundSAMLMessage());
         messageContext.ensureSubcontext(SAMLPeerEntityContext.class).setEntityId(issuer);
         messageContext.ensureSubcontext(SAMLPeerEntityContext.class).setRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
         messageContext.ensureSubcontext(SAMLProtocolContext.class).setProtocol(SAMLConstants.SAML20P_NS);
         messageContext.ensureSubcontext(SecurityParametersContext.class).setSignatureValidationParameters(sigValParams);
+
+        handler = new SAML2HTTPRedirectDeflateSignatureSecurityHandler();
+        handler.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
+        handler.initialize();
     }
     
     /**
@@ -258,18 +270,27 @@ public class SAML2HTTPRedirectDeflateSignatureSecurityHandlerTest extends XMLObj
      * Test context issuer set, invalid signature with trusted credential.
      * 
      * @throws MessageHandlerException ...
+     * @throws UnsupportedEncodingException ...
      */
     @Test(expectedExceptions=MessageHandlerException.class)
-    public void testInvalidSignature() throws MessageHandlerException {
+    public void testInvalidSignature() throws MessageHandlerException, UnsupportedEncodingException {
         trustedCredentials.add(signingX509Cred);
         
+        // Note: this is just for posterity and clarity as to what's going on. Can't manipulate the request anymore to cause signature failure,
+        // since the signed content is now obtained from the message context, as populated by the decoder.
         final MockHttpServletRequest request = (MockHttpServletRequest) handler.getHttpServletRequest();
         final String queryString = request.getQueryString();
         assert queryString != null;
         request.setQueryString(queryString.replaceFirst("RelayState=", "RelayState=AlteredData"));
-        // Really only the query string is necessary to cause failure, but just to be safe...
         request.setParameter("RelayState", "AlteredData" + request.getParameter("RelayState") );
         
+        // This actually causes the expected signature failure
+        final String origSignedContent = new String(messageContext.ensureSubcontext(SimpleSignatureContext.class).getSignedContent(), "UTF-8");
+        final String badSignedContent = origSignedContent.replaceFirst("RelayState=", "RelayState=AlteredData");
+        //System.err.println("Actual:   " + origSignedContent);
+        //System.err.println("Expected: " + badSignedContent);
+        messageContext.ensureSubcontext(SimpleSignatureContext.class).setSignedContent(badSignedContent.getBytes());
+        
         handler.invoke(messageContext);
     }
     

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


More information about the commits mailing list