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.ByteArrayInputStream;
20  import java.io.UnsupportedEncodingException;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import javax.servlet.http.HttpServletRequest;
25  
26  import org.opensaml.common.binding.SAMLMessageContext;
27  import org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule;
28  import org.opensaml.ws.security.SecurityPolicyException;
29  import org.opensaml.xml.Configuration;
30  import org.opensaml.xml.io.Unmarshaller;
31  import org.opensaml.xml.io.UnmarshallingException;
32  import org.opensaml.xml.parse.ParserPool;
33  import org.opensaml.xml.parse.XMLParserException;
34  import org.opensaml.xml.security.CriteriaSet;
35  import org.opensaml.xml.security.SecurityException;
36  import org.opensaml.xml.security.credential.Credential;
37  import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
38  import org.opensaml.xml.security.keyinfo.KeyInfoCriteria;
39  import org.opensaml.xml.signature.KeyInfo;
40  import org.opensaml.xml.signature.SignatureTrustEngine;
41  import org.opensaml.xml.util.Base64;
42  import org.opensaml.xml.util.DatatypeHelper;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  import org.w3c.dom.Document;
46  
47  /**
48   * Security policy which evaluates simple "blob" signatures according to the SAML 2 HTTP-POST-SimpleSign binding.
49   */
50  public class SAML2HTTPPostSimpleSignRule extends BaseSAMLSimpleSignatureSecurityPolicyRule {
51  
52      /** Logger. */
53      private Logger log = LoggerFactory.getLogger(SAML2HTTPPostSimpleSignRule.class);
54  
55      /** Parser pool to use to process KeyInfo request parameter. */
56      private ParserPool parser;
57  
58      /** KeyInfo resolver to use to process KeyInfo request parameter. */
59      private KeyInfoCredentialResolver keyInfoResolver;
60  
61      /**
62       * Constructor.
63       * 
64       * @param engine the trust engine to use
65       * @param parserPool the parser pool used to parse the KeyInfo request parameter
66       * @param keyInfoCredResolver the KeyInfo credential resovler to use to extract credentials from the KeyInfo request
67       *            parameter
68       */
69      public SAML2HTTPPostSimpleSignRule(SignatureTrustEngine engine, ParserPool parserPool,
70              KeyInfoCredentialResolver keyInfoCredResolver) {
71          super(engine);
72          parser = parserPool;
73          keyInfoResolver = keyInfoCredResolver;
74      }
75  
76      /** {@inheritDoc} */
77      protected boolean ruleHandles(HttpServletRequest request, SAMLMessageContext samlMsgCtx) {
78          return "POST".equals(request.getMethod());
79      }
80  
81      /** {@inheritDoc} */
82      protected byte[] getSignedContent(HttpServletRequest request) throws SecurityPolicyException {
83          StringBuilder builder = new StringBuilder();
84          String samlMsg;
85          try {
86              if (request.getParameter("SAMLRequest") != null) {
87                  samlMsg = new String(Base64.decode(request.getParameter("SAMLRequest")), "UTF-8");
88                  builder.append("SAMLRequest=" + samlMsg);
89              } else if (request.getParameter("SAMLResponse") != null) {
90                  samlMsg = new String(Base64.decode(request.getParameter("SAMLResponse")), "UTF-8");
91                  builder.append("SAMLResponse=" + samlMsg);
92              } else {
93                  log.warn("Could not extract either a SAMLRequest or a SAMLResponse from the form control data");
94                  throw new SecurityPolicyException("Extract of SAMLRequest or SAMLResponse from form control data");
95              }
96          } catch (UnsupportedEncodingException e) {
97              // All JVM's required to support UTF-8
98          }
99  
100         if (request.getParameter("RelayState") != null) {
101             builder.append("&RelayState=" + request.getParameter("RelayState"));
102         }
103 
104         builder.append("&SigAlg=" + request.getParameter("SigAlg"));
105 
106         String constructed = builder.toString();
107         if (DatatypeHelper.isEmpty(constructed)) {
108             log.warn("Could not construct signed content string from form control data");
109             return null;
110         }
111         log.debug("Constructed signed content string for HTTP-Post-SimpleSign {}", constructed);
112 
113         try {
114             return constructed.getBytes("UTF-8");
115         } catch (UnsupportedEncodingException e) {
116             // All JVM's required to support UTF-8
117         }
118         return null;
119     }
120 
121     /** {@inheritDoc} */
122     protected List<Credential> getRequestCredentials(HttpServletRequest request, SAMLMessageContext samlContext)
123             throws SecurityPolicyException {
124 
125         String kiBase64 = request.getParameter("KeyInfo");
126         if (DatatypeHelper.isEmpty(kiBase64)) {
127             log.debug("Form control data did not contain a KeyInfo");
128             return null;
129         } else {
130             log.debug("Found a KeyInfo in form control data, extracting validation credentials");
131         }
132 
133         Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory()
134                 .getUnmarshaller(KeyInfo.DEFAULT_ELEMENT_NAME);
135         if (unmarshaller == null) {
136             throw new SecurityPolicyException("Could not obtain a KeyInfo unmarshaller");
137         }
138 
139         ByteArrayInputStream is = new ByteArrayInputStream(Base64.decode(kiBase64));
140         KeyInfo keyInfo = null;
141         try {
142             Document doc = parser.parse(is);
143             keyInfo = (KeyInfo) unmarshaller.unmarshall(doc.getDocumentElement());
144         } catch (XMLParserException e) {
145             log.warn("Error parsing KeyInfo data", e);
146             throw new SecurityPolicyException("Error parsing KeyInfo data", e);
147         } catch (UnmarshallingException e) {
148             log.warn("Error unmarshalling KeyInfo data", e);
149             throw new SecurityPolicyException("Error unmarshalling KeyInfo data", e);
150         }
151 
152         if (keyInfo == null) {
153             log.warn("Could not successfully extract KeyInfo object from the form control data");
154             return null;
155         }
156 
157         List<Credential> credentials = new ArrayList<Credential>();
158         CriteriaSet criteriaSet = new CriteriaSet(new KeyInfoCriteria(keyInfo));
159         try {
160             for (Credential cred : keyInfoResolver.resolve(criteriaSet)) {
161                 credentials.add(cred);
162             }
163         } catch (SecurityException e) {
164             log.warn("Error resolving credentials from KeyInfo", e);
165             throw new SecurityPolicyException("Error resolving credentials from KeyInfo", e);
166         }
167 
168         return credentials;
169     }
170 
171 }