View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security;
21  
22  import org.apache.ws.security.components.crypto.Crypto;
23  import org.apache.ws.security.conversation.ConversationConstants;
24  import org.apache.ws.security.handler.RequestData;
25  import org.apache.ws.security.message.CallbackLookup;
26  import org.apache.ws.security.processor.Processor;
27  import org.apache.ws.security.util.WSSecurityUtil;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Element;
30  import org.w3c.dom.Node;
31  
32  import javax.security.auth.callback.CallbackHandler;
33  import javax.xml.namespace.QName;
34  
35  import java.util.ArrayList;
36  import java.util.List;
37  
38  /**
39   * WS-Security Engine.
40   * <p/>
41   *
42   * @author Davanum Srinivas (dims@yahoo.com).
43   * @author Werner Dittmann (Werner.Dittmann@t-online.de).
44   */
45  public class WSSecurityEngine {
46      private static org.apache.commons.logging.Log log = 
47          org.apache.commons.logging.LogFactory.getLog(WSSecurityEngine.class);
48  
49      /**
50       * The WSSConfig instance used by this SecurityEngine to
51       * find Processors for processing security headers
52       */
53      private WSSConfig wssConfig = null;
54      private boolean doDebug = false;
55      private CallbackLookup callbackLookup = null;
56      /**
57       * <code>wsse:BinarySecurityToken</code> as defined by WS Security specification
58       */
59      public static final QName BINARY_TOKEN = 
60          new QName(WSConstants.WSSE_NS, WSConstants.BINARY_TOKEN_LN);
61      /**
62       * <code>wsse:UsernameToken</code> as defined by WS Security specification
63       */
64      public static final QName USERNAME_TOKEN = 
65          new QName(WSConstants.WSSE_NS, WSConstants.USERNAME_TOKEN_LN);
66      /**
67       * <code>wsu:Timestamp</code> as defined by OASIS WS Security specification,
68       */
69      public static final QName TIMESTAMP = 
70          new QName(WSConstants.WSU_NS, WSConstants.TIMESTAMP_TOKEN_LN);
71      /**
72       * <code>wsse11:signatureConfirmation</code> as defined by OASIS WS Security specification,
73       */
74      public static final QName SIGNATURE_CONFIRMATION = 
75          new QName(WSConstants.WSSE11_NS, WSConstants.SIGNATURE_CONFIRMATION_LN);
76      /**
77       * <code>ds:Signature</code> as defined by XML Signature specification,
78       * enhanced by WS Security specification
79       */
80      public static final QName SIGNATURE = 
81          new QName(WSConstants.SIG_NS, WSConstants.SIG_LN);
82      /**
83       * <code>xenc:EncryptedKey</code> as defined by XML Encryption specification,
84       * enhanced by WS Security specification
85       */
86      public static final QName ENCRYPTED_KEY = 
87          new QName(WSConstants.ENC_NS, WSConstants.ENC_KEY_LN);
88      /**
89       * <code>xenc:EncryptedData</code> as defined by XML Encryption specification,
90       * enhanced by WS Security specification
91       */
92      public static final QName ENCRYPTED_DATA = 
93          new QName(WSConstants.ENC_NS, WSConstants.ENC_DATA_LN);
94      /**
95       * <code>xenc:ReferenceList</code> as defined by XML Encryption specification,
96       */
97      public static final QName REFERENCE_LIST = 
98          new QName(WSConstants.ENC_NS, WSConstants.REF_LIST_LN);
99      /**
100      * <code>saml:Assertion</code> as defined by SAML v1.1 specification
101      */
102     public static final QName SAML_TOKEN = 
103         new QName(WSConstants.SAML_NS, WSConstants.ASSERTION_LN);
104     
105     /**
106      * <code>saml:Assertion</code> as defined by SAML v2.0 specification
107      */
108     public static final QName SAML2_TOKEN = 
109         new QName(WSConstants.SAML2_NS, WSConstants.ASSERTION_LN);
110 
111     /**
112      * <code>wsc:DerivedKeyToken</code> as defined by WS-SecureConversation specification
113      */
114     public static final QName DERIVED_KEY_TOKEN_05_02 = 
115         new QName(ConversationConstants.WSC_NS_05_02, ConversationConstants.DERIVED_KEY_TOKEN_LN);
116 
117     /**
118      * <code>wsc:SecurityContextToken</code> as defined by WS-SecureConversation specification
119      */
120     public static final QName SECURITY_CONTEXT_TOKEN_05_02 = 
121         new QName(ConversationConstants.WSC_NS_05_02, ConversationConstants.SECURITY_CONTEXT_TOKEN_LN);
122 
123     /**
124      * <code>wsc:DerivedKeyToken</code> as defined by WS-SecureConversation specification in WS-SX
125      */
126     public static final QName DERIVED_KEY_TOKEN_05_12 = 
127         new QName(ConversationConstants.WSC_NS_05_12, ConversationConstants.DERIVED_KEY_TOKEN_LN);
128 
129     /**
130      * <code>wsc:SecurityContextToken</code> as defined by WS-SecureConversation specification in 
131      * WS-SX
132      */
133     public static final QName SECURITY_CONTEXT_TOKEN_05_12 = 
134         new QName(ConversationConstants.WSC_NS_05_12, ConversationConstants.SECURITY_CONTEXT_TOKEN_LN);
135     
136     /**
137      * @return      the WSSConfig object set on this instance
138      */
139     public final WSSConfig
140     getWssConfig() {
141         if (wssConfig == null) {
142             wssConfig = WSSConfig.getNewInstance();
143         }
144         return wssConfig;
145     }
146     
147     /**
148      * @param cfg   the WSSConfig instance for this WSSecurityEngine to use
149      *
150      * @return      the WSSConfig instance previously set on this 
151      *              WSSecurityEngine instance
152      */
153     public final WSSConfig
154     setWssConfig(WSSConfig cfg) {
155         WSSConfig ret = wssConfig;
156         wssConfig = cfg;
157         return ret;
158     }
159     
160     /**
161      * Set the CallbackLookup object to use to locate elements
162      * @param callbackLookup the CallbackLookup object to use to locate elements
163      */
164     public void setCallbackLookup(CallbackLookup callbackLookup) {
165         this.callbackLookup = callbackLookup;
166     }
167     
168     /**
169      * Get the CallbackLookup object to use to locate elements
170      * @return the CallbackLookup object to use to locate elements
171      */
172     public CallbackLookup getCallbackLookup() {
173         return callbackLookup;
174     }
175     
176     /**
177      * Process the security header given the soap envelope as W3C document.
178      * <p/>
179      * This is the main entry point to verify or decrypt a SOAP envelope.
180      * First check if a <code>wsse:Security</code> is available with the
181      * defined actor.
182      *
183      * @param doc    the SOAP envelope as {@link Document}
184      * @param actor  the engine works on behalf of this <code>actor</code>. Refer
185      *               to the SOAP specification about <code>actor</code> or <code>role
186      *               </code>
187      * @param cb     a callback hander to the caller to resolve passwords during
188      *               encryption and UsernameToken handling
189      * @param crypto the object that implements the access to the keystore and the
190      *               handling of certificates.
191      * @return a result list
192      * @throws WSSecurityException
193      * @see WSSecurityEngine#processSecurityHeader(Element securityHeader, CallbackHandler cb,
194      * Crypto sigCrypto, Crypto decCrypto)
195      */
196     public List<WSSecurityEngineResult> processSecurityHeader(
197         Document doc,
198         String actor,
199         CallbackHandler cb,
200         Crypto crypto
201     ) throws WSSecurityException {
202         return processSecurityHeader(doc, actor, cb, crypto, crypto);
203     }
204 
205     /**
206      * Process the security header given the soap envelope as W3C document.
207      * <p/>
208      * This is the main entry point to verify or decrypt a SOAP envelope.
209      * First check if a <code>wsse:Security</code> is available with the
210      * defined actor.
211      *
212      * @param doc       the SOAP envelope as {@link Document}
213      * @param actor     the engine works on behalf of this <code>actor</code>. Refer
214      *                  to the SOAP specification about <code>actor</code> or <code>role
215      *                  </code>
216      * @param cb        a callback hander to the caller to resolve passwords during
217      *                  encryption and UsernameToken handling
218      * @param sigCrypto the object that implements the access to the keystore and the
219      *                  handling of certificates for Signature
220      * @param decCrypto the object that implements the access to the keystore and the
221      *                  handling of certificates for Decryption
222      * @return a result list
223      * @throws WSSecurityException
224      * @see WSSecurityEngine#processSecurityHeader(
225      * Element securityHeader, CallbackHandler cb, Crypto sigCrypto, Crypto decCrypto)
226      */
227     public List<WSSecurityEngineResult> processSecurityHeader(
228         Document doc,
229         String actor,
230         CallbackHandler cb,
231         Crypto sigCrypto,
232         Crypto decCrypto
233     ) throws WSSecurityException {
234         doDebug = log.isDebugEnabled();
235         if (doDebug) {
236             log.debug("enter processSecurityHeader()");
237         }
238 
239         if (actor == null) {
240             actor = "";
241         }
242         List<WSSecurityEngineResult> wsResult = null;
243         Element elem = WSSecurityUtil.getSecurityHeader(doc, actor);
244         if (elem != null) {
245             if (doDebug) {
246                 log.debug("Processing WS-Security header for '" + actor + "' actor.");
247             }
248             wsResult = processSecurityHeader(elem, cb, sigCrypto, decCrypto);
249         }
250         return wsResult;
251     }
252 
253     /**
254      * Process the security header given the <code>wsse:Security</code> DOM
255      * Element. 
256      * 
257      * This function loops over all direct child elements of the
258      * <code>wsse:Security</code> header. If it finds a known element, it
259      * transfers control to the appropriate handling function. The method
260      * processes the known child elements in the same order as they appear in
261      * the <code>wsse:Security</code> element. This is in accordance to the WS
262      * Security specification. <p/>
263      * 
264      * Currently the functions can handle the following child elements:
265      * 
266      * <ul>
267      * <li>{@link #SIGNATURE <code>ds:Signature</code>}</li>
268      * <li>{@link #ENCRYPTED_KEY <code>xenc:EncryptedKey</code>}</li>
269      * <li>{@link #REFERENCE_LIST <code>xenc:ReferenceList</code>}</li>
270      * <li>{@link #USERNAME_TOKEN <code>wsse:UsernameToken</code>}</li>
271      * <li>{@link #TIMESTAMP <code>wsu:Timestamp</code>}</li>
272      * </ul>
273      *
274      * Note that additional child elements can be processed if appropriate
275      * Processors have been registered with the WSSCondig instance set
276      * on this class.
277      *
278      * @param securityHeader the <code>wsse:Security</code> header element
279      * @param cb             a callback hander to the caller to resolve passwords during
280      *                       encryption and UsernameToken handling
281      * @param sigCrypto      the object that implements the access to the keystore and the
282      *                       handling of certificates used for Signature
283      * @param decCrypto      the object that implements the access to the keystore and the
284      *                       handling of certificates used for Decryption
285      * @return a List of {@link WSSecurityEngineResult}. Each element in the
286      *         the List represents the result of a security action. The elements
287      *         are ordered according to the sequence of the security actions in the
288      *         wsse:Signature header. The List may be empty if no security processing
289      *         was performed.
290      * @throws WSSecurityException
291      */
292     public List<WSSecurityEngineResult> processSecurityHeader(
293         Element securityHeader,
294         CallbackHandler cb,
295         Crypto sigCrypto,
296         Crypto decCrypto
297     ) throws WSSecurityException { 
298         RequestData data = new RequestData();
299         data.setWssConfig(getWssConfig());
300         data.setDecCrypto(decCrypto);
301         data.setSigCrypto(sigCrypto);
302         data.setCallbackHandler(cb);
303         return processSecurityHeader(securityHeader, data);
304     }
305     
306     
307     /**
308      * Process the security header given the <code>wsse:Security</code> DOM
309      * Element. 
310      * 
311      * This function loops over all direct child elements of the
312      * <code>wsse:Security</code> header. If it finds a known element, it
313      * transfers control to the appropriate handling function. The method
314      * processes the known child elements in the same order as they appear in
315      * the <code>wsse:Security</code> element. This is in accordance to the WS
316      * Security specification. <p/>
317      * 
318      * Currently the functions can handle the following child elements:
319      * 
320      * <ul>
321      * <li>{@link #SIGNATURE <code>ds:Signature</code>}</li>
322      * <li>{@link #ENCRYPTED_KEY <code>xenc:EncryptedKey</code>}</li>
323      * <li>{@link #REFERENCE_LIST <code>xenc:ReferenceList</code>}</li>
324      * <li>{@link #USERNAME_TOKEN <code>wsse:UsernameToken</code>}</li>
325      * <li>{@link #TIMESTAMP <code>wsu:Timestamp</code>}</li>
326      * </ul>
327      *
328      * Note that additional child elements can be processed if appropriate
329      * Processors have been registered with the WSSCondig instance set
330      * on this class.
331      *
332      * @param securityHeader the <code>wsse:Security</code> header element
333      * @param requestData    the RequestData associated with the request.  It should
334      *                       be able to provide the callback handler, cryptos, etc...
335      *                       as needed by the processing
336      * @return a List of {@link WSSecurityEngineResult}. Each element in the
337      *         the List represents the result of a security action. The elements
338      *         are ordered according to the sequence of the security actions in the
339      *         wsse:Signature header. The List may be empty if no security processing
340      *         was performed.
341      * @throws WSSecurityException
342      */
343     public List<WSSecurityEngineResult> processSecurityHeader(
344         Element securityHeader,
345         RequestData requestData) throws WSSecurityException {
346         List<WSSecurityEngineResult> returnResults = new ArrayList<WSSecurityEngineResult>();
347         if (securityHeader == null) {
348             return returnResults;
349         }
350     
351         if (requestData.getWssConfig() == null) {
352             requestData.setWssConfig(getWssConfig());
353         }
354         
355         //
356         // Gather some info about the document to process and store
357         // it for retrieval. Store the implementation of signature crypto
358         // (no need for encryption --- yet)
359         //
360         WSDocInfo wsDocInfo = new WSDocInfo(securityHeader.getOwnerDocument());
361         wsDocInfo.setCallbackLookup(callbackLookup);
362         wsDocInfo.setCrypto(requestData.getSigCrypto());
363         wsDocInfo.setSecurityHeader(securityHeader);
364 
365         final WSSConfig cfg = getWssConfig();
366         Node node = securityHeader.getFirstChild();
367         
368         boolean foundTimestamp = false;
369         while (node != null) {
370             Node nextSibling = node.getNextSibling();
371             if (Node.ELEMENT_NODE == node.getNodeType()) {
372                 QName el = new QName(node.getNamespaceURI(), node.getLocalName());
373                 
374                 // Check for multiple timestamps
375                 if (requestData.getWssConfig().isWsiBSPCompliant()) {
376                     if (foundTimestamp && el.equals(TIMESTAMP)) {
377                         if (doDebug) {
378                             log.debug(
379                                 "Failure on processing multiple Timestamps as per the BSP"
380                             );
381                         }
382                         throw new WSSecurityException(
383                             WSSecurityException.INVALID_SECURITY_TOKEN, "invalidTimestamp"
384                         );
385                     } else if (el.equals(TIMESTAMP)) {
386                         foundTimestamp = true;
387                     }
388                 }
389                 //
390                 // Call the processor for this token. After the processor returns, 
391                 // store it for later retrieval. The token processor may store some
392                 // information about the processed token
393                 //
394                 Processor p = cfg.getProcessor(el);
395                 if (p != null) {
396                     List<WSSecurityEngineResult> results = 
397                         p.handleToken((Element) node, requestData, wsDocInfo);
398                     returnResults.addAll(0, results);
399                 } else {
400                     if (doDebug) {
401                         log.debug(
402                             "Unknown Element: " + node.getLocalName() + " " + node.getNamespaceURI()
403                         );
404                     }
405                 }
406             }
407             //
408             // If the next sibling is null and the stored next sibling is not null, then we have
409             // encountered an EncryptedData element which was decrypted, and so the next sibling
410             // of the current node is null. In that case, go on to the previously stored next
411             // sibling
412             //
413             if (node.getNextSibling() == null && nextSibling != null) {
414                 node = nextSibling;
415             } else {
416                 node = node.getNextSibling();
417             }
418         }
419         
420         return returnResults;
421     }
422 }