1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.dom.util;
21
22 import org.apache.wss4j.dom.SOAP11Constants;
23 import org.apache.wss4j.dom.SOAP12Constants;
24 import org.apache.wss4j.dom.SOAPConstants;
25 import org.apache.wss4j.dom.WSConstants;
26 import org.apache.wss4j.dom.callback.CallbackLookup;
27 import org.apache.wss4j.dom.engine.WSSConfig;
28 import org.apache.wss4j.common.WSEncryptionPart;
29 import org.apache.wss4j.common.ext.WSSecurityException;
30 import org.apache.wss4j.common.util.AttachmentUtils;
31 import org.apache.wss4j.common.util.XMLUtils;
32 import org.apache.wss4j.dom.handler.HandlerAction;
33 import org.apache.wss4j.dom.handler.RequestData;
34 import org.apache.wss4j.dom.handler.WSHandlerConstants;
35 import org.w3c.dom.Attr;
36 import org.w3c.dom.Document;
37 import org.w3c.dom.Element;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.Text;
40
41
42
43 import java.lang.reflect.AccessibleObject;
44 import java.lang.reflect.InvocationTargetException;
45 import java.lang.reflect.Method;
46 import java.security.AccessController;
47 import java.security.PrivilegedAction;
48 import java.security.PrivilegedActionException;
49 import java.security.PrivilegedExceptionAction;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.List;
53
54 import javax.security.auth.callback.CallbackHandler;
55
56
57
58
59
60 public final class WSSecurityUtil {
61
62 private static boolean isSAAJ14 = false;
63
64 private static final org.slf4j.Logger LOG =
65 org.slf4j.LoggerFactory.getLogger(WSSecurityUtil.class);
66
67 private static final ClassValue<Method> GET_DOM_ELEMENTS_METHODS = new ClassValue<Method>() {
68 @Override
69 protected Method computeValue(Class<?> type) {
70 try {
71 return getMethod(type, "getDomElement");
72 } catch (NoSuchMethodException e) {
73
74 return null;
75 }
76 }
77 };
78
79 private static final ClassValue<Method> GET_ENVELOPE_METHODS = new ClassValue<Method>() {
80 @Override
81 protected Method computeValue(Class<?> type) {
82 try {
83 return getMethod(type, "getEnvelope");
84 } catch (NoSuchMethodException e) {
85
86 return null;
87 }
88 }
89 };
90
91 static {
92 try {
93 Method[] methods = WSSecurityUtil.class.getClassLoader().
94 loadClass("com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl").getMethods();
95 for (Method method : methods) {
96 if (method.getName().equals("register")) {
97
98 isSAAJ14 = true;
99 break;
100 }
101 }
102 } catch (ClassNotFoundException cnfe) {
103 LOG.debug("Can't load class com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl", cnfe);
104
105 try {
106 Method[] methods = WSSecurityUtil.class.getClassLoader().
107 loadClass("com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl").getMethods();
108 for (Method method : methods) {
109 if (method.getName().equals("register")) {
110
111 isSAAJ14 = true;
112 break;
113 }
114 }
115 } catch (ClassNotFoundException cnfe1) {
116 LOG.debug("can't load class com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl", cnfe1);
117 }
118 }
119 }
120
121 private WSSecurityUtil() {
122
123 }
124
125 private static Method getMethod(final Class<?> clazz, final String name,
126 final Class<?>... parameterTypes) throws NoSuchMethodException {
127 try {
128 return AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
129 public Method run() throws Exception {
130 return clazz.getMethod(name, parameterTypes);
131 }
132 });
133 } catch (PrivilegedActionException pae) {
134 Exception e = pae.getException();
135 if (e instanceof NoSuchMethodException) {
136 throw (NoSuchMethodException)e;
137 }
138 throw new SecurityException(e);
139 }
140 }
141
142 private static <T extends AccessibleObject> T setAccessible(final T o) {
143 return AccessController.doPrivileged(new PrivilegedAction<T>() {
144 public T run() {
145 o.setAccessible(true);
146 return o;
147 }
148 });
149 }
150
151 public static Element getSOAPHeader(Document doc) {
152 String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
153 return
154 XMLUtils.getDirectChildElement(
155 doc.getDocumentElement(), WSConstants.ELEM_HEADER, soapNamespace
156 );
157 }
158
159
160
161
162
163
164
165
166
167
168 public static Element getSecurityHeader(Document doc, String actor) throws WSSecurityException {
169 Element soapHeaderElement = getSOAPHeader(doc);
170 if (soapHeaderElement == null) {
171 return null;
172 }
173
174 String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
175 return getSecurityHeader(soapHeaderElement, actor, WSConstants.URI_SOAP12_ENV.equals(soapNamespace));
176 }
177
178
179
180
181
182 public static Element getSecurityHeader(Element soapHeader, String actor, boolean soap12)
183 throws WSSecurityException {
184
185 String actorLocal = WSConstants.ATTR_ACTOR;
186 String soapNamespace = WSConstants.URI_SOAP11_ENV;
187 if (soap12) {
188 actorLocal = WSConstants.ATTR_ROLE;
189 soapNamespace = WSConstants.URI_SOAP12_ENV;
190 }
191
192
193
194
195 Element foundSecurityHeader = null;
196 for (
197 Node currentChild = soapHeader.getFirstChild();
198 currentChild != null;
199 currentChild = currentChild.getNextSibling()
200 ) {
201 if (Node.ELEMENT_NODE == currentChild.getNodeType()
202 && WSConstants.WSSE_LN.equals(currentChild.getLocalName())
203 && (WSConstants.WSSE_NS.equals(currentChild.getNamespaceURI())
204 || WSConstants.OLD_WSSE_NS.equals(currentChild.getNamespaceURI()))) {
205
206 Element elem = (Element)currentChild;
207 Attr attr = elem.getAttributeNodeNS(soapNamespace, actorLocal);
208 String hActor = (attr != null) ? attr.getValue() : null;
209
210 if (WSSecurityUtil.isActorEqual(actor, hActor)) {
211 if (foundSecurityHeader != null) {
212 LOG.debug(
213 "Two or more security headers have the same actor name: {}", actor
214 );
215 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
216 }
217 foundSecurityHeader = elem;
218 }
219 }
220 }
221 return foundSecurityHeader;
222 }
223
224
225
226
227
228
229
230
231
232
233 public static boolean isActorEqual(String actor, String hActor) {
234 if ((hActor == null || hActor.length() == 0)
235 && (actor == null || actor.length() == 0)) {
236 return true;
237 }
238
239 return hActor != null && actor != null && hActor.equalsIgnoreCase(actor);
240 }
241
242
243
244
245
246
247
248
249
250 public static List<Element> getDirectChildElements(
251 Node fNode,
252 String localName,
253 String namespace
254 ) {
255 List<Element> children = new ArrayList<>();
256 for (
257 Node currentChild = fNode.getFirstChild();
258 currentChild != null;
259 currentChild = currentChild.getNextSibling()
260 ) {
261 if (Node.ELEMENT_NODE == currentChild.getNodeType()
262 && localName.equals(currentChild.getLocalName())
263 && namespace.equals(currentChild.getNamespaceURI())) {
264 children.add((Element)currentChild);
265 }
266 }
267 return children;
268 }
269
270
271
272
273
274
275
276
277
278 public static Element findBodyElement(Document doc) {
279 Element docElement = doc.getDocumentElement();
280 String ns = docElement.getNamespaceURI();
281 return XMLUtils.getDirectChildElement(docElement, WSConstants.ELEM_BODY, ns);
282 }
283
284
285
286
287
288
289
290
291
292
293 public static List<Element> findElements(
294 WSEncryptionPart part, CallbackLookup callbackLookup
295 ) throws WSSecurityException {
296
297 if (part.getElement() != null) {
298 return Collections.singletonList(part.getElement());
299 }
300
301
302 String id = part.getId();
303 if (id != null) {
304 Element foundElement = callbackLookup.getElement(id, null, false);
305 return Collections.singletonList(foundElement);
306 }
307
308 return callbackLookup.getElements(part.getName(), part.getNamespace());
309 }
310
311
312
313
314
315
316 public static WSEncryptionPart getDefaultEncryptionPart(Document doc) {
317 String soapNamespace =
318 WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
319 return new WSEncryptionPart(WSConstants.ELEM_BODY, soapNamespace, "Content");
320 }
321
322
323
324
325
326
327
328
329 private static Element createElementInSameNamespace(Node parent, String localName) {
330 String qName = localName;
331 String prefix = parent.getPrefix();
332 if (prefix != null && prefix.length() > 0) {
333 qName = prefix + ":" + localName;
334 }
335
336 String nsUri = parent.getNamespaceURI();
337 return parent.getOwnerDocument().createElementNS(nsUri, qName);
338 }
339
340
341
342
343
344
345
346
347
348
349
350 public static Element prependChildElement(
351 Element parent,
352 Element child
353 ) {
354 Node firstChild = parent.getFirstChild();
355 Element domChild = null;
356 try {
357 domChild = (Element)getDomElement(child);
358 } catch (WSSecurityException e) {
359 LOG.debug("Error when try to get Dom Element from the child", e);
360 }
361 if (firstChild == null) {
362 return (Element)parent.appendChild(domChild);
363 } else {
364 return (Element)parent.insertBefore(domChild, firstChild);
365 }
366 }
367
368
369
370
371
372
373
374
375
376
377 public static Element findWsseSecurityHeaderBlock(
378 Document doc,
379 Element envelope,
380 boolean doCreate
381 ) throws WSSecurityException {
382 return findWsseSecurityHeaderBlock(doc, envelope, null, doCreate);
383 }
384
385
386
387
388
389
390
391
392
393
394 public static Element findWsseSecurityHeaderBlock(
395 Document doc,
396 Element envelope,
397 String actor,
398 boolean doCreate
399 ) throws WSSecurityException {
400 String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
401 Element header =
402 XMLUtils.getDirectChildElement(
403 doc.getDocumentElement(),
404 WSConstants.ELEM_HEADER,
405 soapNamespace
406 );
407 if (header == null) {
408 if (doCreate) {
409 if (isSAAJ14) {
410 try {
411 Node node = null;
412 Method method = GET_ENVELOPE_METHODS.get(doc.getClass());
413 if (method != null) {
414 try {
415 node = (Node)setAccessible(method).invoke(doc);
416 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
417 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
418 }
419 }
420 if (node != null) {
421 header = createElementInSameNamespace(node, WSConstants.ELEM_HEADER);
422 } else {
423 header = createElementInSameNamespace(doc.getDocumentElement(), WSConstants.ELEM_HEADER);
424 }
425 header = (Element)doc.importNode(header, true);
426 header = (Element)getDomElement(header);
427 header = prependChildElement(envelope, header);
428
429 } catch (Exception e) {
430 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
431 }
432
433 } else {
434 header = createElementInSameNamespace(envelope, WSConstants.ELEM_HEADER);
435 header = prependChildElement(envelope, header);
436 }
437 } else {
438 return null;
439 }
440 }
441
442 String actorLocal = WSConstants.ATTR_ACTOR;
443 if (WSConstants.URI_SOAP12_ENV.equals(soapNamespace)) {
444 actorLocal = WSConstants.ATTR_ROLE;
445 }
446
447
448
449
450 Element foundSecurityHeader = null;
451 for (
452 Node currentChild = header.getFirstChild();
453 currentChild != null;
454 currentChild = currentChild.getNextSibling()
455 ) {
456 if (Node.ELEMENT_NODE == currentChild.getNodeType()
457 && WSConstants.WSSE_LN.equals(currentChild.getLocalName())
458 && WSConstants.WSSE_NS.equals(currentChild.getNamespaceURI())) {
459
460 Element elem = (Element)currentChild;
461 Attr attr = elem.getAttributeNodeNS(soapNamespace, actorLocal);
462 String hActor = (attr != null) ? attr.getValue() : null;
463
464 if (WSSecurityUtil.isActorEqual(actor, hActor)) {
465 if (foundSecurityHeader != null) {
466 LOG.debug(
467 "Two or more security headers have the same actor name: {}", actor
468 );
469 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
470 }
471 foundSecurityHeader = elem;
472 }
473 }
474 }
475 if (foundSecurityHeader != null) {
476 return foundSecurityHeader;
477 } else if (doCreate) {
478 foundSecurityHeader = doc.createElementNS(WSConstants.WSSE_NS, "wsse:Security");
479 foundSecurityHeader.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsse", WSConstants.WSSE_NS);
480 foundSecurityHeader = (Element)doc.importNode(foundSecurityHeader, true);
481 foundSecurityHeader = (Element)getDomElement(foundSecurityHeader);
482
483 return prependChildElement(header, foundSecurityHeader);
484 }
485 return null;
486 }
487
488
489
490
491
492
493
494
495 public static Text createBase64EncodedTextNode(Document doc, byte[] data) {
496 return doc.createTextNode(org.apache.xml.security.utils.XMLUtils.encodeToString(data));
497 }
498
499 public static SOAPConstants getSOAPConstants(Element startElement) {
500 Document doc = startElement.getOwnerDocument();
501 String ns = doc.getDocumentElement().getNamespaceURI();
502 if (WSConstants.URI_SOAP12_ENV.equals(ns)) {
503 return new SOAP12Constants();
504 }
505 return new SOAP11Constants();
506 }
507
508 public static String getSOAPNamespace(Element startElement) {
509 return getSOAPConstants(startElement).getEnvelopeURI();
510 }
511
512 public static List<Integer> decodeAction(String action) throws WSSecurityException {
513 String actionToParse = action;
514 if (actionToParse == null) {
515 return Collections.emptyList();
516 }
517 actionToParse = actionToParse.trim();
518 if (actionToParse.length() == 0) {
519 return Collections.emptyList();
520 }
521
522 List<Integer> actions = new ArrayList<>();
523 String[] single = actionToParse.split("\\s");
524 for (String parsedAction : single) {
525 if (parsedAction.equals(WSHandlerConstants.NO_SECURITY)) {
526 return Collections.emptyList();
527 } else if (parsedAction.equals(WSHandlerConstants.USERNAME_TOKEN)) {
528 actions.add(WSConstants.UT);
529 } else if (parsedAction.equals(WSHandlerConstants.USERNAME_TOKEN_NO_PASSWORD)) {
530 actions.add(WSConstants.UT_NOPASSWORD);
531 } else if (parsedAction.equals(WSHandlerConstants.SIGNATURE)) {
532 actions.add(WSConstants.SIGN);
533 } else if (parsedAction.equals(WSHandlerConstants.SIGNATURE_DERIVED)) {
534 actions.add(WSConstants.DKT_SIGN);
535 } else if (parsedAction.equals(WSHandlerConstants.ENCRYPT)
536 || parsedAction.equals(WSHandlerConstants.ENCRYPTION)) {
537 actions.add(WSConstants.ENCR);
538 } else if (parsedAction.equals(WSHandlerConstants.ENCRYPT_DERIVED)
539 || parsedAction.equals(WSHandlerConstants.ENCRYPTION_DERIVED)) {
540 actions.add(WSConstants.DKT_ENCR);
541 } else if (parsedAction.equals(WSHandlerConstants.SAML_TOKEN_UNSIGNED)) {
542 actions.add(WSConstants.ST_UNSIGNED);
543 } else if (parsedAction.equals(WSHandlerConstants.SAML_TOKEN_SIGNED)) {
544 actions.add(WSConstants.ST_SIGNED);
545 } else if (parsedAction.equals(WSHandlerConstants.TIMESTAMP)) {
546 actions.add(WSConstants.TS);
547 } else if (parsedAction.equals(WSHandlerConstants.USERNAME_TOKEN_SIGNATURE)) {
548 actions.add(WSConstants.UT_SIGN);
549 } else if (parsedAction.equals(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION)) {
550 actions.add(WSConstants.SC);
551 } else if (parsedAction.equals(WSHandlerConstants.CUSTOM_TOKEN)) {
552 actions.add(WSConstants.CUSTOM_TOKEN);
553 } else {
554 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
555 new Object[] {"Unknown action defined: " + parsedAction}
556 );
557 }
558 }
559 return actions;
560 }
561
562
563
564
565
566
567
568
569
570 public static List<HandlerAction> decodeHandlerAction(
571 String action,
572 WSSConfig wssConfig
573 ) throws WSSecurityException {
574 if (action == null) {
575 return Collections.emptyList();
576 }
577
578 List<HandlerAction> actions = new ArrayList<>();
579 String[] single = action.split(" ");
580 for (String parsedAction : single) {
581 if (parsedAction.equals(WSHandlerConstants.NO_SECURITY)) {
582 return actions;
583 } else if (parsedAction.equals(WSHandlerConstants.USERNAME_TOKEN)) {
584 actions.add(new HandlerAction(WSConstants.UT));
585 } else if (parsedAction.equals(WSHandlerConstants.USERNAME_TOKEN_NO_PASSWORD)) {
586 actions.add(new HandlerAction(WSConstants.UT_NOPASSWORD));
587 } else if (parsedAction.equals(WSHandlerConstants.SIGNATURE)) {
588 actions.add(new HandlerAction(WSConstants.SIGN));
589 } else if (parsedAction.equals(WSHandlerConstants.SIGNATURE_DERIVED)) {
590 actions.add(new HandlerAction(WSConstants.DKT_SIGN));
591 } else if (parsedAction.equals(WSHandlerConstants.ENCRYPT)
592 || parsedAction.equals(WSHandlerConstants.ENCRYPTION)) {
593 actions.add(new HandlerAction(WSConstants.ENCR));
594 } else if (parsedAction.equals(WSHandlerConstants.ENCRYPT_DERIVED)
595 || parsedAction.equals(WSHandlerConstants.ENCRYPTION_DERIVED)) {
596 actions.add(new HandlerAction(WSConstants.DKT_ENCR));
597 } else if (parsedAction.equals(WSHandlerConstants.SAML_TOKEN_UNSIGNED)) {
598 actions.add(new HandlerAction(WSConstants.ST_UNSIGNED));
599 } else if (parsedAction.equals(WSHandlerConstants.SAML_TOKEN_SIGNED)) {
600 actions.add(new HandlerAction(WSConstants.ST_SIGNED));
601 } else if (parsedAction.equals(WSHandlerConstants.TIMESTAMP)) {
602 actions.add(new HandlerAction(WSConstants.TS));
603 } else if (parsedAction.equals(WSHandlerConstants.USERNAME_TOKEN_SIGNATURE)) {
604 actions.add(new HandlerAction(WSConstants.UT_SIGN));
605 } else if (parsedAction.equals(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION)) {
606 actions.add(new HandlerAction(WSConstants.SC));
607 } else if (parsedAction.equals(WSHandlerConstants.CUSTOM_TOKEN)) {
608 actions.add(new HandlerAction(WSConstants.CUSTOM_TOKEN));
609 } else {
610 try {
611 int customAction = Integer.parseInt(parsedAction);
612 if (wssConfig == null || wssConfig.getAction(customAction) == null) {
613 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
614 new Object[] {"Unknown action defined: " + parsedAction}
615 );
616 }
617 actions.add(new HandlerAction(customAction));
618 } catch (NumberFormatException ex) {
619 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
620 new Object[] {"Unknown action defined: " + parsedAction}
621 );
622 }
623 }
624 }
625 return actions;
626 }
627
628 public static void inlineAttachments(List<Element> includeElements,
629 CallbackHandler attachmentCallbackHandler,
630 boolean removeAttachments) throws WSSecurityException {
631 for (Element includeElement : includeElements) {
632 String xopURI = includeElement.getAttributeNS(null, "href");
633 if (xopURI != null) {
634
635 byte[] attachmentBytes =
636 WSSecurityUtil.getBytesFromAttachment(xopURI, attachmentCallbackHandler, removeAttachments);
637 String encodedBytes = org.apache.xml.security.utils.XMLUtils.encodeToString(attachmentBytes);
638
639 Node encodedChild =
640 includeElement.getOwnerDocument().createTextNode(encodedBytes);
641 includeElement.getParentNode().replaceChild(encodedChild, includeElement);
642 }
643 }
644 }
645
646
647
648
649
650
651
652
653 public static Element cloneElement(Document doc, Element clonedElement) throws WSSecurityException {
654 clonedElement = (Element)clonedElement.cloneNode(true);
655 if (isSAAJ14) {
656
657 clonedElement = (Element)doc.importNode(clonedElement, true);
658 clonedElement = (Element)getDomElement(clonedElement);
659 }
660 return clonedElement;
661 }
662
663
664
665
666
667
668
669 private static Node getDomElement(Node node) throws WSSecurityException {
670 if (node != null && isSAAJ14) {
671
672 Method method = GET_DOM_ELEMENTS_METHODS.get(node.getClass());
673 if (method != null) {
674 try {
675 return (Node)setAccessible(method).invoke(node);
676 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
677 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
678 }
679 }
680 }
681 return node;
682 }
683
684 public static byte[] getBytesFromAttachment(
685 String xopUri, RequestData data
686 ) throws WSSecurityException {
687 return getBytesFromAttachment(xopUri, data.getAttachmentCallbackHandler());
688 }
689
690 public static byte[] getBytesFromAttachment(
691 String xopUri, CallbackHandler attachmentCallbackHandler
692 ) throws WSSecurityException {
693 return getBytesFromAttachment(xopUri, attachmentCallbackHandler, true);
694 }
695
696 public static byte[] getBytesFromAttachment(
697 String xopUri, CallbackHandler attachmentCallbackHandler, boolean removeAttachments
698 ) throws WSSecurityException {
699 return AttachmentUtils.getBytesFromAttachment(xopUri, attachmentCallbackHandler, removeAttachments);
700 }
701
702 public static String getAttachmentId(String xopUri) throws WSSecurityException {
703 return AttachmentUtils.getAttachmentId(xopUri);
704 }
705
706 }