[java-opensaml] branch main updated: OSJ-427: Simple signature verification fails to detect parameter ...
Brent Putman
putmanb at georgetown.edu
Sat Mar 22 02:57:51 UTC 2025
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch main
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=816a81c21a92c52f96875dbc0442bc2fb5f0d5a8
The following commit(s) were added to refs/heads/main by this push:
new 816a81c21 OSJ-427: Simple signature verification fails to detect parameter ...
816a81c21 is described below
commit 816a81c21a92c52f96875dbc0442bc2fb5f0d5a8
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 f8eabbdb7..fb19a8e73 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;
@@ -137,7 +138,7 @@ public abstract class BaseSAMLSimpleSignatureSecurityHandler extends AbstractHtt
}
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;
@@ -364,10 +365,16 @@ public abstract class BaseSAMLSimpleSignatureSecurityHandler extends AbstractHtt
* 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