View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.saml2.binding.security;
18  
19  import java.io.UnsupportedEncodingException;
20  
21  import javax.servlet.http.HttpServletRequest;
22  
23  import org.opensaml.common.binding.SAMLMessageContext;
24  import org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule;
25  import org.opensaml.ws.security.SecurityPolicyException;
26  import org.opensaml.ws.transport.http.HTTPTransportUtils;
27  import org.opensaml.xml.signature.SignatureTrustEngine;
28  import org.opensaml.xml.util.DatatypeHelper;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * Security policy which evaluates simple "blob" signatures according to the SAML 2 HTTP-Redirect DEFLATE binding.
34   */
35  public class SAML2HTTPRedirectDeflateSignatureRule extends BaseSAMLSimpleSignatureSecurityPolicyRule {
36  
37      /** Logger. */
38      private final Logger log = LoggerFactory.getLogger(SAML2HTTPRedirectDeflateSignatureRule.class);
39  
40      /**
41       * Constructor.
42       * 
43       * @param engine the trust engine to use
44       */
45      public SAML2HTTPRedirectDeflateSignatureRule(SignatureTrustEngine engine) {
46          super(engine);
47      }
48  
49      /** {@inheritDoc} */
50      protected boolean ruleHandles(HttpServletRequest request, SAMLMessageContext samlMsgCtx)
51              throws SecurityPolicyException {
52          return "GET".equals(request.getMethod());
53      }
54  
55      /** {@inheritDoc} */
56      protected byte[] getSignedContent(HttpServletRequest request) throws SecurityPolicyException {
57          // We need the raw non-URL-decoded query string param values for HTTP-Redirect DEFLATE simple signature
58          // validation.
59          // We have to construct a string containing the signature input by accessing the
60          // request directly. We can't use the decoded parameters because we need the raw
61          // data and URL-encoding isn't canonical.
62          String queryString = request.getQueryString();
63          log.debug("Constructing signed content string from URL query string {}", queryString);
64  
65          String constructed = buildSignedContentString(queryString);
66          if (DatatypeHelper.isEmpty(constructed)) {
67              log.warn("Could not extract signed content string from query string");
68              return null;
69          }
70          log.debug("Constructed signed content string for HTTP-Redirect DEFLATE {}", constructed);
71  
72          try {
73              return constructed.getBytes("UTF-8");
74          } catch (UnsupportedEncodingException e) {
75              // JVM is required to support UTF-8
76          }
77          return null;
78      }
79  
80      /**
81       * Extract the raw request parameters and build a string representation of the content that was signed.
82       * 
83       * @param queryString the raw HTTP query string from the request
84       * @return a string representation of the signed content
85       * @throws SecurityPolicyException thrown if there is an error during request processing
86       */
87      private String buildSignedContentString(String queryString) throws SecurityPolicyException {
88          StringBuilder builder = new StringBuilder();
89  
90          // One of these two is mandatory
91          if (!appendParameter(builder, queryString, "SAMLRequest")) {
92              if (!appendParameter(builder, queryString, "SAMLResponse")) {
93                  log.warn("Could not extract either a SAMLRequest or a SAMLResponse from the query string");
94                  throw new SecurityPolicyException("Extract of SAMLRequest or SAMLResponse from query string failed");
95              }
96          }
97          // This is optional
98          appendParameter(builder, queryString, "RelayState");
99          // This is mandatory, but has already been checked in superclass
100         appendParameter(builder, queryString, "SigAlg");
101 
102         return builder.toString();
103     }
104 
105     /**
106      * Find the raw query string parameter indicated and append it to the string builder.
107      * 
108      * The appended value will be in the form 'paramName=paramValue' (minus the quotes).
109      * 
110      * @param builder string builder to which to append the parameter
111      * @param queryString the URL query string containing parameters
112      * @param paramName the name of the parameter to append
113      * @return true if parameter was found, false otherwise
114      */
115     private boolean appendParameter(StringBuilder builder, String queryString, String paramName) {
116         String rawParam = HTTPTransportUtils.getRawQueryStringParameter(queryString, paramName);
117         if (rawParam == null) {
118             return false;
119         }
120 
121         if (builder.length() > 0) {
122             builder.append('&');
123         }
124 
125         builder.append(rawParam);
126 
127         return true;
128     }
129 }