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 }