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.util;
21  
22  import org.apache.ws.security.SOAP11Constants;
23  import org.apache.ws.security.SOAP12Constants;
24  import org.apache.ws.security.SOAPConstants;
25  import org.apache.ws.security.WSConstants;
26  import org.apache.ws.security.WSDataRef;
27  import org.apache.ws.security.WSEncryptionPart;
28  import org.apache.ws.security.WSSecurityEngineResult;
29  import org.apache.ws.security.WSSecurityException;
30  import org.apache.ws.security.WSSConfig;
31  import org.apache.ws.security.handler.WSHandlerConstants;
32  import org.apache.ws.security.message.CallbackLookup;
33  import org.apache.xml.security.algorithms.JCEMapper;
34  import org.w3c.dom.Attr;
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.NamedNodeMap;
38  import org.w3c.dom.Node;
39  import org.w3c.dom.Text;
40  
41  import javax.crypto.Cipher;
42  import javax.crypto.NoSuchPaddingException;
43  import javax.crypto.SecretKey;
44  import javax.crypto.spec.SecretKeySpec;
45  import javax.xml.crypto.dom.DOMCryptoContext;
46  import javax.xml.namespace.QName;
47  
48  import java.security.MessageDigest;
49  import java.security.NoSuchAlgorithmException;
50  import java.security.SecureRandom;
51  import java.util.ArrayList;
52  import java.util.Collections;
53  import java.util.HashSet;
54  import java.util.Iterator;
55  import java.util.List;
56  import java.util.Set;
57  
58  /**
59   * WS-Security Utility methods. <p/>
60   * 
61   * @author Davanum Srinivas (dims@yahoo.com).
62   */
63  public final class WSSecurityUtil {
64      private static org.apache.commons.logging.Log log = 
65          org.apache.commons.logging.LogFactory.getLog(WSSecurityUtil.class);
66  
67      /**
68       * A cached pseudo-random number generator
69       * NB. On some JVMs, caching this random number
70       * generator is required to overcome punitive
71       * overhead.
72       */
73      private static SecureRandom random = null;
74      
75      /**
76       * A cached MessageDigest object
77       */
78      private static MessageDigest digest = null;
79      
80      private WSSecurityUtil() {
81          // Complete
82      }
83      
84      /**
85       * Returns the first WS-Security header element for a given actor. Only one
86       * WS-Security header is allowed for an actor.
87       * 
88       * @param doc
89       * @param actor
90       * @return the <code>wsse:Security</code> element or <code>null</code>
91       *         if not such element found
92       */
93      public static Element getSecurityHeader(Document doc, String actor) throws WSSecurityException {
94          String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
95          Element soapHeaderElement = 
96              getDirectChildElement(
97                  doc.getDocumentElement(), 
98                  WSConstants.ELEM_HEADER, 
99                  soapNamespace
100             );
101         if (soapHeaderElement == null) { // no SOAP header at all
102             return null;
103         }
104         
105         String actorLocal = WSConstants.ATTR_ACTOR;
106         if (WSConstants.URI_SOAP12_ENV.equals(soapNamespace)) {
107             actorLocal = WSConstants.ATTR_ROLE;
108         }
109         
110         //
111         // Iterate through the security headers
112         //
113         Element foundSecurityHeader = null;
114         for (
115             Node currentChild = soapHeaderElement.getFirstChild(); 
116             currentChild != null; 
117             currentChild = currentChild.getNextSibling()
118         ) {
119             if (Node.ELEMENT_NODE == currentChild.getNodeType()
120                 && WSConstants.WSSE_LN.equals(currentChild.getLocalName())
121                 && WSConstants.WSSE_NS.equals(currentChild.getNamespaceURI())) {
122                 
123                 Element elem = (Element)currentChild;
124                 Attr attr = elem.getAttributeNodeNS(soapNamespace, actorLocal);
125                 String hActor = (attr != null) ? attr.getValue() : null;
126 
127                 if (WSSecurityUtil.isActorEqual(actor, hActor)) {
128                     if (foundSecurityHeader != null) {
129                         if (log.isDebugEnabled()) {
130                             log.debug(
131                                 "Two or more security headers have the same actor name: " + actor
132                             );
133                         }
134                         throw new WSSecurityException(WSSecurityException.INVALID_SECURITY);
135                     }
136                     foundSecurityHeader = elem;
137                 }
138             }
139         }
140         return foundSecurityHeader;
141     }
142 
143 
144     /**
145      * Compares two actor strings and returns true if these are equal. Takes
146      * care of the null length strings and uses ignore case.
147      * 
148      * @param actor
149      * @param hActor
150      * @return true is the actor arguments are equal
151      */
152     public static boolean isActorEqual(String actor, String hActor) {
153         if (((hActor == null) || (hActor.length() == 0)) 
154             && ((actor == null) || (actor.length() == 0))) {
155             return true;
156         }
157         
158         if ((hActor != null) && (actor != null) && hActor.equalsIgnoreCase(actor)) {
159             return true;
160         }
161         
162         return false;
163     }
164 
165     
166     /**
167      * Gets a direct child with specified localname and namespace. <p/>
168      * 
169      * @param parentNode the node where to start the search
170      * @param localName local name of the child to get
171      * @param namespace the namespace of the child to get
172      * @return the node or <code>null</code> if not such node found
173      */
174     public static Element getDirectChildElement(
175         Node parentNode, 
176         String localName,
177         String namespace
178     ) {
179         if (parentNode == null) {
180             return null;
181         }
182         for (
183             Node currentChild = parentNode.getFirstChild(); 
184             currentChild != null; 
185             currentChild = currentChild.getNextSibling()
186         ) {
187             if (Node.ELEMENT_NODE == currentChild.getNodeType()
188                 && localName.equals(currentChild.getLocalName())
189                 && namespace.equals(currentChild.getNamespaceURI())) {
190                 return (Element)currentChild;
191             }
192         }
193         return null;
194     }
195     
196     
197     /**
198      * Gets all direct children with specified localname and namespace. <p/>
199      * 
200      * @param fNode the node where to start the search
201      * @param localName local name of the children to get
202      * @param namespace the namespace of the children to get
203      * @return the list of nodes or <code>null</code> if not such nodes are found
204      */
205     public static List<Element> getDirectChildElements(
206         Node fNode, 
207         String localName,
208         String namespace
209     ) {
210         List<Element> children = new ArrayList<Element>();
211         for (
212             Node currentChild = fNode.getFirstChild(); 
213             currentChild != null; 
214             currentChild = currentChild.getNextSibling()
215         ) {
216             if (Node.ELEMENT_NODE == currentChild.getNodeType()
217                 && localName.equals(currentChild.getLocalName())
218                 && namespace.equals(currentChild.getNamespaceURI())) {
219                 children.add((Element)currentChild);
220             }
221         }
222         return children;
223     }
224     
225 
226     /**
227      * return the first soap "Body" element. <p/>
228      * 
229      * @param doc
230      * @return the body element or <code>null</code> if document does not
231      *         contain a SOAP body
232      */
233     public static Element findBodyElement(Document doc) {
234         //
235         // Find the SOAP Envelope NS. Default to SOAP11 NS
236         //
237         Element docElement = doc.getDocumentElement();
238         String ns = docElement.getNamespaceURI();
239         return getDirectChildElement(docElement, WSConstants.ELEM_BODY, ns);
240     }
241     
242     
243     /**
244      * Find the DOM Element in the SOAP Envelope that is referenced by the 
245      * WSEncryptionPart argument. The "Id" is used before the Element localname/namespace.
246      * 
247      * @param part The WSEncryptionPart object corresponding to the DOM Element(s) we want
248      * @param callbackLookup The CallbackLookup object used to find Elements
249      * @param doc The owning document
250      * @return the DOM Element in the SOAP Envelope that is found
251      */
252     public static List<Element> findElements(
253         WSEncryptionPart part, CallbackLookup callbackLookup, Document doc
254     ) throws WSSecurityException {
255         // See if the DOM Element is stored in the WSEncryptionPart first
256         if (part.getElement() != null) {
257             return Collections.singletonList(part.getElement());
258         }
259         
260         // Next try to find the Element via its wsu:Id
261         String id = part.getId();
262         if (id != null) {
263             Element foundElement = callbackLookup.getElement(id, null, false);
264             return Collections.singletonList(foundElement);
265         }
266         // Otherwise just lookup all elements with the localname/namespace
267         return callbackLookup.getElements(part.getName(), part.getNamespace());
268     }
269     
270     /**
271      * Returns the first element that matches <code>name</code> and
272      * <code>namespace</code>. <p/> This is a replacement for a XPath lookup
273      * <code>//name</code> with the given namespace. It's somewhat faster than
274      * XPath, and we do not deal with prefixes, just with the real namespace URI
275      * 
276      * @param startNode Where to start the search
277      * @param name Local name of the element
278      * @param namespace Namespace URI of the element
279      * @return The found element or <code>null</code>
280      */
281     public static Element findElement(Node startNode, String name, String namespace) {
282         //
283         // Replace the formerly recursive implementation with a depth-first-loop
284         // lookup
285         //
286         if (startNode == null) {
287             return null;
288         }
289         Node startParent = startNode.getParentNode();
290         Node processedNode = null;
291 
292         while (startNode != null) {
293             // start node processing at this point
294             if (startNode.getNodeType() == Node.ELEMENT_NODE
295                 && startNode.getLocalName().equals(name)) {
296                 String ns = startNode.getNamespaceURI();
297                 if (ns != null && ns.equals(namespace)) {
298                     return (Element)startNode;
299                 }
300 
301                 if ((namespace == null || namespace.length() == 0)
302                     && (ns == null || ns.length() == 0)) {
303                     return (Element)startNode;
304                 }
305             }
306             processedNode = startNode;
307             startNode = startNode.getFirstChild();
308 
309             // no child, this node is done.
310             if (startNode == null) {
311                 // close node processing, get sibling
312                 startNode = processedNode.getNextSibling();
313             }
314             // no more siblings, get parent, all children
315             // of parent are processed.
316             while (startNode == null) {
317                 processedNode = processedNode.getParentNode();
318                 if (processedNode == startParent) {
319                     return null;
320                 }
321                 // close parent node processing (processed node now)
322                 startNode = processedNode.getNextSibling();
323             }
324         }
325         return null;
326     }
327     
328     /**
329      * Returns all elements that match <code>name</code> and <code>namespace</code>. 
330      * <p/> This is a replacement for a XPath lookup
331      * <code>//name</code> with the given namespace. It's somewhat faster than
332      * XPath, and we do not deal with prefixes, just with the real namespace URI
333      * 
334      * @param startNode Where to start the search
335      * @param name Local name of the element
336      * @param namespace Namespace URI of the element
337      * @return The found elements (or an empty list)
338      */
339     public static List<Element> findElements(Node startNode, String name, String namespace) {
340         //
341         // Replace the formerly recursive implementation with a depth-first-loop
342         // lookup
343         //
344         if (startNode == null) {
345             return null;
346         }
347         Node startParent = startNode.getParentNode();
348         Node processedNode = null;
349 
350         List<Element> foundNodes = new ArrayList<Element>();
351         while (startNode != null) {
352             // start node processing at this point
353             if (startNode.getNodeType() == Node.ELEMENT_NODE
354                 && startNode.getLocalName().equals(name)) {
355                 String ns = startNode.getNamespaceURI();
356                 if (ns != null && ns.equals(namespace)) {
357                     foundNodes.add((Element)startNode);
358                 }
359 
360                 if ((namespace == null || namespace.length() == 0)
361                     && (ns == null || ns.length() == 0)) {
362                     foundNodes.add((Element)startNode);
363                 }
364             }
365             processedNode = startNode;
366             startNode = startNode.getFirstChild();
367 
368             // no child, this node is done.
369             if (startNode == null) {
370                 // close node processing, get sibling
371                 startNode = processedNode.getNextSibling();
372             }
373             // no more siblings, get parent, all children
374             // of parent are processed.
375             while (startNode == null) {
376                 processedNode = processedNode.getParentNode();
377                 if (processedNode == startParent) {
378                     return foundNodes;
379                 }
380                 // close parent node processing (processed node now)
381                 startNode = processedNode.getNextSibling();
382             }
383         }
384         return foundNodes;
385     }
386     
387     /**
388      * Returns the single SAMLAssertion element that contains an AssertionID/ID that
389      * matches the supplied parameter.
390      * 
391      * @param startNode Where to start the search
392      * @param value Value of the AssertionID/ID attribute
393      * @return The found element if there was exactly one match, or
394      *         <code>null</code> otherwise
395      */
396     public static Element findSAMLAssertionElementById(Node startNode, String value) {
397         Element foundElement = null;
398 
399         //
400         // Replace the formerly recursive implementation with a depth-first-loop
401         // lookup
402         //
403         if (startNode == null) {
404             return null;
405         }
406         Node startParent = startNode.getParentNode();
407         Node processedNode = null;
408 
409         while (startNode != null) {
410             // start node processing at this point
411             if (startNode.getNodeType() == Node.ELEMENT_NODE) {
412                 Element se = (Element) startNode;
413                 if ((se.hasAttribute("ID") && value.equals(se.getAttributeNS(null, "ID")))
414                     || (se.hasAttribute("AssertionID") 
415                         && value.equals(se.getAttributeNS(null, "AssertionID")))) {
416                     if (foundElement == null) {
417                         foundElement = se; // Continue searching to find duplicates
418                     } else {
419                         log.warn("Multiple elements with the same 'ID' attribute value!");
420                         return null;
421                     }
422                 }
423             }
424 
425             processedNode = startNode;
426             startNode = startNode.getFirstChild();
427 
428             // no child, this node is done.
429             if (startNode == null) {
430                 // close node processing, get sibling
431                 startNode = processedNode.getNextSibling();
432             }
433             // no more siblings, get parent, all children
434             // of parent are processed.
435             while (startNode == null) {
436                 processedNode = processedNode.getParentNode();
437                 if (processedNode == startParent) {
438                     return foundElement;
439                 }
440                 // close parent node processing (processed node now)
441                 startNode = processedNode.getNextSibling();
442             }
443         }
444         return foundElement;
445     }
446     
447 
448     /**
449      * Returns the single element that contains an Id with value
450      * <code>uri</code> and <code>namespace</code>. The Id can be either a wsu:Id or an Id
451      * with no namespace. This is a replacement for a XPath Id lookup with the given namespace. 
452      * It's somewhat faster than XPath, and we do not deal with prefixes, just with the real
453      * namespace URI
454      * 
455      * If checkMultipleElements is true and there are multiple elements, we log a 
456      * warning and return null as this can be used to get around the signature checking.
457      * 
458      * @param startNode Where to start the search
459      * @param value Value of the Id attribute
460      * @param checkMultipleElements If true then go through the entire tree and return 
461      *        null if there are multiple elements with the same Id
462      * @return The found element if there was exactly one match, or
463      *         <code>null</code> otherwise
464      */
465     public static Element findElementById(
466         Node startNode, String value, boolean checkMultipleElements
467     ) {
468         //
469         // Replace the formerly recursive implementation with a depth-first-loop lookup
470         //
471         Node startParent = startNode.getParentNode();
472         Node processedNode = null;
473         Element foundElement = null;
474         String id = getIDFromReference(value);
475 
476         while (startNode != null) {
477             // start node processing at this point
478             if (startNode.getNodeType() == Node.ELEMENT_NODE) {
479                 Element se = (Element) startNode;
480                 // Try the wsu:Id first
481                 String attributeNS = se.getAttributeNS(WSConstants.WSU_NS, "Id");
482                 if ("".equals(attributeNS) || !id.equals(attributeNS)) {
483                     attributeNS = se.getAttributeNS(null, "Id");
484                 }
485                 if (!"".equals(attributeNS) && id.equals(attributeNS)) {
486                     if (!checkMultipleElements) {
487                         return se;
488                     } else if (foundElement == null) {
489                         foundElement = se; // Continue searching to find duplicates
490                     } else {
491                         log.warn("Multiple elements with the same 'Id' attribute value!");
492                         return null;
493                     }
494                 }
495             }
496 
497             processedNode = startNode;
498             startNode = startNode.getFirstChild();
499 
500             // no child, this node is done.
501             if (startNode == null) {
502                 // close node processing, get sibling
503                 startNode = processedNode.getNextSibling();
504             }
505             // no more siblings, get parent, all children
506             // of parent are processed.
507             while (startNode == null) {
508                 processedNode = processedNode.getParentNode();
509                 if (processedNode == startParent) {
510                     return foundElement;
511                 }
512                 // close parent node processing (processed node now)
513                 startNode = processedNode.getNextSibling();
514             }
515         }
516         return foundElement;
517     }
518 
519     /**
520      * Set a namespace/prefix on an element if it is not set already. First off, it
521      * searches for the element for the prefix associated with the specified
522      * namespace. If the prefix isn't null, then this is returned. Otherwise, it
523      * creates a new attribute using the namespace/prefix passed as parameters.
524      * 
525      * @param element
526      * @param namespace
527      * @param prefix
528      * @return the prefix associated with the set namespace
529      */
530     public static String setNamespace(Element element, String namespace, String prefix) {
531         String pre = getPrefixNS(namespace, element);
532         if (pre != null) {
533             return pre;
534         }
535         element.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + prefix, namespace);
536         return prefix;
537     }
538 
539     /*
540      * The following methods were copied over from axis.utils.XMLUtils and adapted
541      */
542     public static String getPrefixNS(String uri, Node e) {
543         while (e != null && (e.getNodeType() == Element.ELEMENT_NODE)) {
544             NamedNodeMap attrs = e.getAttributes();
545             for (int n = 0; n < attrs.getLength(); n++) {
546                 Attr a = (Attr) attrs.item(n);
547                 String name = a.getName();
548                 if (name.startsWith("xmlns:") && a.getNodeValue().equals(uri)) {
549                     return name.substring("xmlns:".length());
550                 }
551             }
552             e = e.getParentNode();
553         }
554         return null;
555     }
556 
557     public static String getNamespace(String prefix, Node e) {
558         while (e != null && (e.getNodeType() == Node.ELEMENT_NODE)) {
559             Attr attr = null;
560             if (prefix == null) {
561                 attr = ((Element) e).getAttributeNodeNS(null, "xmlns");
562             } else {
563                 attr = ((Element) e).getAttributeNodeNS(WSConstants.XMLNS_NS, prefix);
564             }
565             if (attr != null) {
566                 return attr.getValue();
567             }
568             e = e.getParentNode();
569         }
570         return null;
571     }
572 
573     /**
574      * Return a QName when passed a string like "foo:bar" by mapping the "foo"
575      * prefix to a namespace in the context of the given Node.
576      * 
577      * @return a QName generated from the given string representation
578      */
579     public static QName getQNameFromString(String str, Node e) {
580         return getQNameFromString(str, e, false);
581     }
582 
583     /**
584      * Return a QName when passed a string like "foo:bar" by mapping the "foo"
585      * prefix to a namespace in the context of the given Node. If default
586      * namespace is found it is returned as part of the QName.
587      * 
588      * @return a QName generated from the given string representation
589      */
590     public static QName getFullQNameFromString(String str, Node e) {
591         return getQNameFromString(str, e, true);
592     }
593 
594     private static QName getQNameFromString(String str, Node e, boolean defaultNS) {
595         if (str == null || e == null) {
596             return null;
597         }
598         int idx = str.indexOf(':');
599         if (idx > -1) {
600             String prefix = str.substring(0, idx);
601             String ns = getNamespace(prefix, e);
602             if (ns == null) {
603                 return null;
604             }
605             return new QName(ns, str.substring(idx + 1));
606         } else {
607             if (defaultNS) {
608                 String ns = getNamespace(null, e);
609                 if (ns != null) {
610                     return new QName(ns, str);
611                 }
612             }
613             return new QName("", str);
614         }
615     }
616 
617     /**
618      * Return a string for a particular QName, mapping a new prefix if
619      * necessary.
620      */
621     public static String getStringForQName(QName qname, Element e) {
622         String uri = qname.getNamespaceURI();
623         String prefix = getPrefixNS(uri, e);
624         if (prefix == null) {
625             int i = 1;
626             prefix = "ns" + i;
627             while (getNamespace(prefix, e) != null) {
628                 i++;
629                 prefix = "ns" + i;
630             }
631             e.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + prefix, uri);
632         }
633         return prefix + ":" + qname.getLocalPart();
634     }
635 
636     /**
637      * Turn a reference (eg "#5") into an ID (eg "5").
638      * 
639      * @param ref
640      * @return ref trimmed and with the leading "#" removed, or null if not
641      *         correctly formed
642      */
643     public static String getIDFromReference(String ref) {
644         String id = ref.trim();
645         if (id.length() == 0) {
646             return null;
647         }
648         if (id.charAt(0) == '#') {
649             id = id.substring(1);
650         }
651         return id;
652     }
653     
654     /**
655      * create a new element in the same namespace <p/>
656      * 
657      * @param parent for the new element
658      * @param localName of the new element
659      * @return the new element
660      */
661     private static Element createElementInSameNamespace(Element parent, String localName) {
662         String qName = localName;
663         String prefix = parent.getPrefix();
664         if (prefix != null && prefix.length() > 0) {
665             qName = prefix + ":" + localName;
666         }
667          
668         String nsUri = parent.getNamespaceURI();
669         return parent.getOwnerDocument().createElementNS(nsUri, qName);
670     }
671 
672 
673     /**
674      * prepend a child element <p/>
675      * 
676      * @param parent element of this child element
677      * @param child the element to append
678      * @return the child element
679      */
680     public static Element prependChildElement(
681         Element parent,
682         Element child
683     ) {
684         Node firstChild = parent.getFirstChild();
685         if (firstChild == null) {
686             return (Element)parent.appendChild(child);
687         } else {
688             return (Element)parent.insertBefore(child, firstChild);
689         }
690     }
691 
692 
693     /**
694      * find the first ws-security header block <p/>
695      * 
696      * @param doc the DOM document (SOAP request)
697      * @param envelope the SOAP envelope
698      * @param doCreate if true create a new WSS header block if none exists
699      * @return the WSS header or null if none found and doCreate is false
700      */
701     public static Element findWsseSecurityHeaderBlock(
702         Document doc,
703         Element envelope, 
704         boolean doCreate
705     ) throws WSSecurityException {
706         return findWsseSecurityHeaderBlock(doc, envelope, null, doCreate);
707     }
708 
709     /**
710      * find a WS-Security header block for a given actor <p/>
711      * 
712      * @param doc the DOM document (SOAP request)
713      * @param envelope the SOAP envelope
714      * @param actor the actor (role) name of the WSS header
715      * @param doCreate if true create a new WSS header block if none exists
716      * @return the WSS header or null if none found and doCreate is false
717      */
718     public static Element findWsseSecurityHeaderBlock(
719         Document doc,
720         Element envelope,
721         String actor, 
722         boolean doCreate
723     ) throws WSSecurityException {
724         String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
725         Element header = 
726             getDirectChildElement(
727                 doc.getDocumentElement(), 
728                 WSConstants.ELEM_HEADER, 
729                 soapNamespace
730             );
731         if (header == null) { // no SOAP header at all
732             if (doCreate) {
733                 header = createElementInSameNamespace(envelope, WSConstants.ELEM_HEADER);
734                 header = prependChildElement(envelope, header);
735             } else {
736                 return null;
737             }
738         }
739         
740         String actorLocal = WSConstants.ATTR_ACTOR;
741         if (WSConstants.URI_SOAP12_ENV.equals(soapNamespace)) {
742             actorLocal = WSConstants.ATTR_ROLE;
743         }
744         
745         //
746         // Iterate through the security headers
747         //
748         Element foundSecurityHeader = null;
749         for (
750             Node currentChild = header.getFirstChild(); 
751             currentChild != null; 
752             currentChild = currentChild.getNextSibling()
753         ) {
754             if (Node.ELEMENT_NODE == currentChild.getNodeType()
755                 && WSConstants.WSSE_LN.equals(currentChild.getLocalName())
756                 && WSConstants.WSSE_NS.equals(currentChild.getNamespaceURI())) {
757                 
758                 Element elem = (Element)currentChild;
759                 Attr attr = elem.getAttributeNodeNS(soapNamespace, actorLocal);
760                 String hActor = (attr != null) ? attr.getValue() : null;
761 
762                 if (WSSecurityUtil.isActorEqual(actor, hActor)) {
763                     if (foundSecurityHeader != null) {
764                         if (log.isDebugEnabled()) {
765                             log.debug(
766                                 "Two or more security headers have the same actor name: " + actor
767                             );
768                         }
769                         throw new WSSecurityException(WSSecurityException.INVALID_SECURITY);
770                     }
771                     foundSecurityHeader = elem;
772                 }
773             }
774         }
775         if (foundSecurityHeader != null) {
776             return foundSecurityHeader;
777         } else if (doCreate) {
778             foundSecurityHeader = doc.createElementNS(WSConstants.WSSE_NS, "wsse:Security");
779             foundSecurityHeader.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsse", WSConstants.WSSE_NS);
780             return prependChildElement(header, foundSecurityHeader);
781         }
782         return null;
783     }
784 
785     /**
786      * create a base64 test node <p/>
787      * 
788      * @param doc the DOM document (SOAP request)
789      * @param data to encode
790      * @return a Text node containing the base64 encoded data
791      */
792     public static Text createBase64EncodedTextNode(Document doc, byte data[]) {
793         return doc.createTextNode(Base64.encode(data));
794     }
795 
796     public static SOAPConstants getSOAPConstants(Element startElement) {
797         Document doc = startElement.getOwnerDocument();
798         String ns = doc.getDocumentElement().getNamespaceURI();
799         if (WSConstants.URI_SOAP12_ENV.equals(ns)) {
800             return new SOAP12Constants();
801         }
802         return new SOAP11Constants();
803     }
804     
805     public static String getSOAPNamespace(Element startElement) {
806         return getSOAPConstants(startElement).getEnvelopeURI();
807     }
808     
809     
810     /**
811      * Convert the raw key bytes into a SecretKey object of type symEncAlgo.
812      */
813     public static SecretKey prepareSecretKey(String symEncAlgo, byte[] rawKey) {
814         // Do an additional check on the keysize required by the encryption algorithm
815         int size = 0;
816         try {
817             size = JCEMapper.getKeyLengthFromURI(symEncAlgo) / 8;
818         } catch (Exception e) {
819             // ignore - some unknown (to JCEMapper) encryption algorithm
820             if (log.isDebugEnabled()) {
821                 log.debug(e.getMessage());
822             }
823         }
824         String keyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(symEncAlgo);
825         SecretKeySpec keySpec;
826         if (size > 0) {
827             keySpec = 
828                 new SecretKeySpec(
829                     rawKey, 0, ((rawKey.length > size) ? size : rawKey.length), keyAlgorithm
830                 );
831         } else {
832             keySpec = new SecretKeySpec(rawKey, keyAlgorithm);
833         }
834         return (SecretKey)keySpec;
835     }
836 
837 
838     /**
839      * Translate the "cipherAlgo" URI to a JCE ID, and return a javax.crypto.Cipher instance
840      * of this type. 
841      */
842     public static Cipher getCipherInstance(String cipherAlgo)
843         throws WSSecurityException {
844         try {
845             String keyAlgorithm = JCEMapper.translateURItoJCEID(cipherAlgo);
846             return Cipher.getInstance(keyAlgorithm);
847         } catch (NoSuchPaddingException ex) {
848             throw new WSSecurityException(
849                 WSSecurityException.UNSUPPORTED_ALGORITHM, "unsupportedKeyTransp", 
850                 new Object[] { "No such padding: " + cipherAlgo }, ex
851             );
852         } catch (NoSuchAlgorithmException ex) {
853             // Check to see if an RSA OAEP MGF-1 with SHA-1 algorithm was requested
854             // Some JDKs don't support RSA/ECB/OAEPPadding
855             if (WSConstants.KEYTRANSPORT_RSAOEP.equals(cipherAlgo)) {
856                 try {
857                     return Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
858                 } catch (Exception e) {
859                     throw new WSSecurityException(
860                         WSSecurityException.UNSUPPORTED_ALGORITHM, "unsupportedKeyTransp",
861                         new Object[] { "No such algorithm: " + cipherAlgo }, e
862                     );
863                 }
864             } else {
865                 throw new WSSecurityException(
866                     WSSecurityException.UNSUPPORTED_ALGORITHM, "unsupportedKeyTransp",
867                     new Object[] { "No such algorithm: " + cipherAlgo }, ex
868                 );
869             }
870         }
871     }
872     
873 
874     /**
875      * Fetch the result of a given action from a given result list
876      * 
877      * @param resultList The result list to fetch an action from
878      * @param action The action to fetch
879      * @return The last result fetched from the result list, null if the result
880      *         could not be found
881      */
882     public static WSSecurityEngineResult fetchActionResult(
883         List<WSSecurityEngineResult> resultList, 
884         int action
885     ) {
886         WSSecurityEngineResult returnResult = null;
887         
888         for (WSSecurityEngineResult result : resultList) {
889             //
890             // Check the result of every action whether it matches the given action
891             //
892             int resultAction = 
893                 ((java.lang.Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
894             if (resultAction == action) {
895                 returnResult = result;
896             }
897         }
898 
899         return returnResult;
900     }
901     
902 
903     /**
904      * Fetch the result of a given action from a given result list.
905      * 
906      * @param resultList The result list to fetch an action from
907      * @param action The action to fetch
908      * @param actionResultList where to store the found results data for the action
909      * @return The result fetched from the result list, null if the result
910      *         could not be found
911      */
912     public static List<WSSecurityEngineResult> fetchAllActionResults(
913         List<WSSecurityEngineResult> resultList,
914         int action, 
915         List<WSSecurityEngineResult> actionResultList
916     ) {
917         for (WSSecurityEngineResult result : resultList) {
918             //
919             // Check the result of every action whether it matches the given action
920             //
921             int resultAction = 
922                 ((java.lang.Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
923             if (resultAction == action) {
924                 actionResultList.add(result);
925             }
926         }
927         return actionResultList;
928     }
929 
930     public static int decodeAction(
931         String action, 
932         List<Integer> actions
933     ) throws WSSecurityException {
934 
935         int doAction = 0;
936         if (action == null) {
937             return doAction;
938         }
939         String single[] = StringUtil.split(action, ' ');
940         for (int i = 0; i < single.length; i++) {
941             if (single[i].equals(WSHandlerConstants.NO_SECURITY)) {
942                 doAction = WSConstants.NO_SECURITY;
943                 return doAction;
944             } else if (single[i].equals(WSHandlerConstants.USERNAME_TOKEN)) {
945                 doAction |= WSConstants.UT;
946                 actions.add(Integer.valueOf(WSConstants.UT));
947             } else if (single[i].equals(WSHandlerConstants.USERNAME_TOKEN_NO_PASSWORD)) {
948                 doAction |= WSConstants.UT_NOPASSWORD;
949                 actions.add(Integer.valueOf(WSConstants.UT_NOPASSWORD));
950             } else if (single[i].equals(WSHandlerConstants.SIGNATURE)) {
951                 doAction |= WSConstants.SIGN;
952                 actions.add(Integer.valueOf(WSConstants.SIGN));
953             } else if (single[i].equals(WSHandlerConstants.ENCRYPT)) {
954                 doAction |= WSConstants.ENCR;
955                 actions.add(Integer.valueOf(WSConstants.ENCR));
956             } else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_UNSIGNED)) {
957                 doAction |= WSConstants.ST_UNSIGNED;
958                 actions.add(Integer.valueOf(WSConstants.ST_UNSIGNED));
959             } else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_SIGNED)) {
960                 doAction |= WSConstants.ST_SIGNED;
961                 actions.add(Integer.valueOf(WSConstants.ST_SIGNED));
962             } else if (single[i].equals(WSHandlerConstants.TIMESTAMP)) {
963                 doAction |= WSConstants.TS;
964                 actions.add(Integer.valueOf(WSConstants.TS));
965             } else if (single[i].equals(WSHandlerConstants.SIGN_WITH_UT_KEY)) {
966                 doAction |= WSConstants.UT_SIGN;
967                 actions.add(Integer.valueOf(WSConstants.UT_SIGN));
968             } else if (single[i].equals(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION)) {
969                 doAction |= WSConstants.SC;
970                 actions.add(Integer.valueOf(WSConstants.SC));
971             } else {
972                 throw new WSSecurityException(
973                     "Unknown action defined: " + single[i]
974                 );
975             }
976         }
977         return doAction;
978     }
979     
980     
981     /**
982      * Decode an action String. This method should only be called on the outbound side.
983      * @param action The initial String of actions to perform
984      * @param actions The list of created actions that will be performed
985      * @param wssConfig This object holds the list of custom actions to be performed.
986      * @return The or'd integer of all the actions (apart from the custom actions)
987      * @throws WSSecurityException
988      */
989     public static int decodeAction(
990         String action, 
991         List<Integer> actions,
992         WSSConfig wssConfig
993     ) throws WSSecurityException {
994 
995         int doAction = 0;
996         if (action == null) {
997             return doAction;
998         }
999         String single[] = StringUtil.split(action, ' ');
1000         for (int i = 0; i < single.length; i++) {
1001             if (single[i].equals(WSHandlerConstants.NO_SECURITY)) {
1002                 doAction = WSConstants.NO_SECURITY;
1003                 return doAction;
1004             } else if (single[i].equals(WSHandlerConstants.USERNAME_TOKEN)) {
1005                 doAction |= WSConstants.UT;
1006                 actions.add(Integer.valueOf(WSConstants.UT));
1007             } else if (single[i].equals(WSHandlerConstants.SIGNATURE)) {
1008                 doAction |= WSConstants.SIGN;
1009                 actions.add(Integer.valueOf(WSConstants.SIGN));
1010             } else if (single[i].equals(WSHandlerConstants.ENCRYPT)) {
1011                 doAction |= WSConstants.ENCR;
1012                 actions.add(Integer.valueOf(WSConstants.ENCR));
1013             } else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_UNSIGNED)) {
1014                 doAction |= WSConstants.ST_UNSIGNED;
1015                 actions.add(Integer.valueOf(WSConstants.ST_UNSIGNED));
1016             } else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_SIGNED)) {
1017                 doAction |= WSConstants.ST_SIGNED;
1018                 actions.add(Integer.valueOf(WSConstants.ST_SIGNED));
1019             } else if (single[i].equals(WSHandlerConstants.TIMESTAMP)) {
1020                 doAction |= WSConstants.TS;
1021                 actions.add(Integer.valueOf(WSConstants.TS));
1022             } else if (single[i].equals(WSHandlerConstants.SIGN_WITH_UT_KEY)) {
1023                 doAction |= WSConstants.UT_SIGN;
1024                 actions.add(Integer.valueOf(WSConstants.UT_SIGN));
1025             } else if (single[i].equals(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION)) {
1026                 doAction |= WSConstants.SC;
1027                 actions.add(Integer.valueOf(WSConstants.SC));
1028             } else {
1029                 try {
1030                     int parsedAction = Integer.parseInt(single[i]);
1031                     if (wssConfig.getAction(parsedAction) == null) {
1032                         throw new WSSecurityException(
1033                             "Unknown action defined: " + single[i]
1034                         );
1035                     }
1036                     actions.add(Integer.valueOf(parsedAction));
1037                 } catch (NumberFormatException ex) {
1038                     throw new WSSecurityException(
1039                         "Unknown action defined: " + single[i]
1040                     );
1041                 }
1042             }
1043         }
1044         return doAction;
1045     }
1046 
1047     /**
1048      * Returns the length of the key in # of bytes
1049      * 
1050      * @param algorithm
1051      * @return the key length
1052      */
1053     public static int getKeyLength(String algorithm) throws WSSecurityException {
1054         if (algorithm.equals(WSConstants.TRIPLE_DES)) {
1055             return 24;
1056         } else if (algorithm.equals(WSConstants.AES_128)) {
1057             return 16;
1058         } else if (algorithm.equals(WSConstants.AES_192)) {
1059             return 24;
1060         } else if (algorithm.equals(WSConstants.AES_256)) {
1061             return 32;
1062         } else if (WSConstants.HMAC_SHA1.equals(algorithm)) {
1063             return 20;
1064         } else if (WSConstants.HMAC_SHA256.equals(algorithm)) {
1065             return 32;
1066         } else if (WSConstants.HMAC_SHA384.equals(algorithm)) {
1067             return 48;
1068         } else if (WSConstants.HMAC_SHA512.equals(algorithm)) {
1069             return 64;
1070         } else if (WSConstants.HMAC_MD5.equals(algorithm)) {
1071             return 16;
1072         } else {
1073             throw new WSSecurityException(
1074                 WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, null
1075             );
1076         }
1077     }
1078 
1079     /**
1080      * Generate a nonce of the given length using the SHA1PRNG algorithm. The SecureRandom
1081      * instance that backs this method is cached for efficiency.
1082      * 
1083      * @return a nonce of the given length
1084      * @throws WSSecurityException
1085      */
1086     public static synchronized byte[] generateNonce(int length) throws WSSecurityException {
1087         try {
1088             if (random == null) {
1089                 random = SecureRandom.getInstance("SHA1PRNG");
1090             }
1091             byte[] temp = new byte[length];
1092             random.nextBytes(temp);
1093             return temp;
1094         } catch (Exception ex) {
1095             throw new WSSecurityException(
1096                 "Error in generating nonce of length " + length, ex
1097             );
1098         }
1099     }
1100     
1101     /**
1102      * Generate a (SHA1) digest of the input bytes. The MessageDigest instance that backs this
1103      * method is cached for efficiency.  
1104      * @param inputBytes the bytes to digest
1105      * @return the digest of the input bytes
1106      * @throws WSSecurityException
1107      */
1108     public static synchronized byte[] generateDigest(byte[] inputBytes) throws WSSecurityException {
1109         try {
1110             if (digest == null) {
1111                 digest = MessageDigest.getInstance("SHA-1");
1112             }
1113             return digest.digest(inputBytes);
1114         } catch (Exception e) {
1115             throw new WSSecurityException(
1116                 "Error in generating digest", e
1117             );
1118         }
1119     }
1120     
1121     /**
1122      * Check that all of the QName[] requiredParts are protected by a specified action in the
1123      * results list.
1124      * @param results The List of WSSecurityEngineResults from processing
1125      * @param action The action that is required (e.g. WSConstants.SIGN)
1126      * @param requiredParts An array of QNames that correspond to the required elements
1127      */
1128     @SuppressWarnings("unchecked")
1129     public static void checkAllElementsProtected(
1130         List<WSSecurityEngineResult> results,
1131         int action,
1132         QName[] requiredParts
1133     ) throws WSSecurityException {
1134         
1135         if (requiredParts != null) {
1136             for (int i = 0; i < requiredParts.length; i++) {
1137                 QName requiredPart = requiredParts[i];
1138                 
1139                 boolean found = false;
1140                 for (Iterator<WSSecurityEngineResult> iter = results.iterator(); 
1141                     iter.hasNext() && !found;) {
1142                     WSSecurityEngineResult result = iter.next();
1143                     int resultAction = 
1144                         ((java.lang.Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
1145                     if (resultAction != action) {
1146                         continue;
1147                     }
1148                     List<WSDataRef> refList = 
1149                         (List<WSDataRef>)result.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
1150                     if (refList != null) {
1151                         for (WSDataRef dataRef : refList) {
1152                             if (dataRef.getName().equals(requiredPart)) {
1153                                 found = true;
1154                                 break;
1155                             }
1156                         }
1157                     }
1158                 }
1159                 if (!found) {
1160                     throw new WSSecurityException(
1161                         WSSecurityException.FAILED_CHECK,
1162                         "requiredElementNotProtected",
1163                         new Object[] {requiredPart}
1164                     );
1165                 }
1166             }
1167             log.debug("All required elements are protected");
1168         }
1169     }
1170 
1171     /**
1172      * Ensure that this covers all required elements (identified by
1173      * their wsu:Id attributes).
1174      * 
1175      * @param resultItem the signature to check
1176      * @param requiredIDs the list of wsu:Id values that must be covered
1177      * @throws WSSecurityException if any required element is not included
1178      */
1179     @SuppressWarnings("unchecked")
1180     public static void checkSignsAllElements(
1181         WSSecurityEngineResult resultItem, 
1182         String[] requiredIDs
1183     ) throws WSSecurityException {
1184         int resultAction = 
1185             ((java.lang.Integer)resultItem.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
1186         if (resultAction != WSConstants.SIGN) {
1187             throw new IllegalArgumentException("Not a SIGN result");
1188         }
1189 
1190         List<WSDataRef> signedElemsRefList = 
1191             (List<WSDataRef>)resultItem.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
1192         if (signedElemsRefList == null) {
1193             throw new WSSecurityException(
1194                 "WSSecurityEngineResult does not contain any references to signed elements"
1195             );
1196         }
1197 
1198         log.debug("Checking required elements are in the signature...");
1199         for (int i = 0; i < requiredIDs.length; i++) {
1200             boolean found = false;
1201             for (int j = 0; j < signedElemsRefList.size(); j++) {
1202                 WSDataRef dataRef = (WSDataRef)signedElemsRefList.get(j);
1203                 String wsuId = dataRef.getWsuId();
1204                 if (wsuId.charAt(0) == '#') {
1205                     wsuId = wsuId.substring(1);
1206                 }
1207                 if (wsuId.equals(requiredIDs[i])) {
1208                     found = true;
1209                 }
1210             }
1211             if (!found) {
1212                 throw new WSSecurityException(
1213                     WSSecurityException.FAILED_CHECK,
1214                     "requiredElementNotSigned",
1215                     new Object[] {requiredIDs[i]}
1216                 );
1217             }
1218             log.debug("Element with ID " + requiredIDs[i] + " was correctly signed");
1219         }
1220         log.debug("All required elements are signed");
1221     }
1222     
1223     
1224     /**
1225      * @return  a list of child Nodes
1226      */
1227     public static List<Node>
1228     listChildren(
1229         final Node parent
1230     ) {
1231         final List<Node> ret = new ArrayList<Node>();
1232         if (parent != null) {
1233             Node node = parent.getFirstChild();
1234             while (node != null) {
1235                 ret.add(node);
1236                 node = node.getNextSibling();
1237             }
1238         }
1239         return ret;
1240     }
1241     
1242     /**
1243      * @return a list of Nodes in b that are not in a 
1244      */
1245     public static List<Node>
1246     newNodes(
1247         final List<Node> a,
1248         final List<Node> b
1249     ) {
1250         if (a.size() == 0) {
1251             return b;
1252         }
1253         final List<Node> ret = new ArrayList<Node>();
1254         if (b.size() == 0) {
1255             return ret;
1256         }
1257         for (
1258             final Iterator<Node> bpos = b.iterator();
1259             bpos.hasNext();
1260         ) {
1261             final Node bnode = bpos.next();
1262             final String bns = bnode.getNamespaceURI();
1263             final String bln = bnode.getLocalName();
1264             boolean found = false;
1265             for (
1266                 final Iterator<Node> apos = a.iterator();
1267                 apos.hasNext() && !found;
1268             ) {
1269                 final Node anode = apos.next();
1270                 final String ans = anode.getNamespaceURI();
1271                 final String aln = anode.getLocalName();
1272                 final boolean nsmatch =
1273                     ans == null
1274                     ? ((bns == null) ? true : false)
1275                     : ((bns == null) ? false : ans.equals(bns));
1276                 final boolean lnmatch =
1277                     aln == null
1278                     ? ((bln == null) ? true : false)
1279                     : ((bln == null) ? false : aln.equals(bln));
1280                 if (nsmatch && lnmatch) {
1281                     found = true;
1282                 }
1283             }
1284             if (!found) {
1285                 ret.add(bnode);
1286             }
1287         }
1288         return ret;
1289     }
1290     
1291     /**
1292      * Store the element argument in the DOM Crypto Context if it has one of the standard
1293      * "Id" attributes that matches the given uri
1294      */
1295     public static void storeElementInContext(
1296         DOMCryptoContext context, 
1297         String uri,
1298         Element element
1299     ) {
1300         String id = uri;
1301         if (uri.charAt(0) == '#') {
1302             id = id.substring(1);
1303         }
1304         
1305         if (element.hasAttributeNS(WSConstants.WSU_NS, "Id")
1306             && id.equals(element.getAttributeNS(WSConstants.WSU_NS, "Id"))) {
1307             context.setIdAttributeNS(element, WSConstants.WSU_NS, "Id");
1308         }
1309         if (element.hasAttributeNS(null, "Id")
1310             && id.equals(element.getAttributeNS(null, "Id"))) {
1311     	    context.setIdAttributeNS(element, null, "Id");
1312         }
1313         if (element.hasAttributeNS(null, "ID")
1314             && id.equals(element.getAttributeNS(null, "ID"))) {
1315             context.setIdAttributeNS(element, null, "ID");
1316         }
1317         if (element.hasAttributeNS(null, "AssertionID")
1318             && id.equals(element.getAttributeNS(null, "AssertionID"))) {
1319             context.setIdAttributeNS(element, null, "AssertionID");
1320         }
1321     }
1322     
1323     /**
1324      * Store the element argument in the DOM Crypto Context if it has one of the standard
1325      * "Id" attributes.
1326      */
1327     public static void storeElementInContext(
1328         DOMCryptoContext context, 
1329         Element element
1330     ) {
1331         if (element.hasAttributeNS(WSConstants.WSU_NS, "Id")) {
1332             context.setIdAttributeNS(element, WSConstants.WSU_NS, "Id");
1333         }
1334         if (element.hasAttributeNS(null, "Id")) {
1335             context.setIdAttributeNS(element, null, "Id");
1336         }
1337         if (element.hasAttributeNS(null, "ID")) {
1338             context.setIdAttributeNS(element, null, "ID");
1339         }
1340         if (element.hasAttributeNS(null, "AssertionID")) {
1341             context.setIdAttributeNS(element, null, "AssertionID");
1342         }
1343     }
1344     
1345     public static void verifySignedElement(Element elem, Document doc, Element securityHeader)
1346         throws WSSecurityException {
1347         final Element envelope = doc.getDocumentElement();
1348         final Set<String> signatureRefIDs = getSignatureReferenceIDs(securityHeader);
1349         if (!signatureRefIDs.isEmpty()) {
1350             Node cur = elem;
1351             while (!cur.isSameNode(envelope)) {
1352                 if (cur.getNodeType() == Node.ELEMENT_NODE) {
1353                     if (WSConstants.SIG_LN.equals(cur.getLocalName())
1354                         && WSConstants.SIG_NS.equals(cur.getNamespaceURI())) {
1355                         throw new WSSecurityException(WSSecurityException.FAILED_CHECK,
1356                             "requiredElementNotSigned", new Object[] {elem});
1357                     } else if (isLinkedBySignatureRefs((Element)cur, signatureRefIDs)) {
1358                         return;
1359                     }
1360                 }
1361                 cur = cur.getParentNode();
1362             }
1363         }
1364         throw new WSSecurityException(
1365             WSSecurityException.FAILED_CHECK, "requiredElementNotSigned", new Object[] {elem});
1366     }
1367     
1368     private static boolean isLinkedBySignatureRefs(Element elem, Set<String> allIDs) {
1369         // Try the wsu:Id first
1370         String attributeNS = elem.getAttributeNS(WSConstants.WSU_NS, "Id");
1371         if (!"".equals(attributeNS) && allIDs.contains(attributeNS)) {
1372             return true;
1373         }
1374         attributeNS = elem.getAttributeNS(null, "Id");
1375         return (!"".equals(attributeNS) && allIDs.contains(attributeNS));
1376     }
1377     
1378     private static Set<String> getSignatureReferenceIDs(Element wsseHeader) throws WSSecurityException {
1379         final Set<String> refs = new HashSet<String>();
1380         final List<Element> signatures = WSSecurityUtil.getDirectChildElements(wsseHeader, WSConstants.SIG_LN, WSConstants.SIG_NS);
1381         for (Element signature : signatures) {
1382             Element sigInfo = WSSecurityUtil.getDirectChildElement(signature, WSConstants.SIG_INFO_LN, WSConstants.SIG_NS);
1383             List<Element> references = WSSecurityUtil.getDirectChildElements(sigInfo, WSConstants.REF_LN, WSConstants.SIG_NS);
1384             for (Element reference : references) {
1385                 String uri = reference.getAttributeNS(null, "URI");
1386                 if (!"".equals(uri)) {
1387                     boolean added = refs.add(WSSecurityUtil.getIDFromReference(uri));
1388                     if (!added) {
1389                         log.warn("Duplicated reference uri: " + uri);
1390                     }
1391                 }
1392             }
1393         }
1394         return refs;
1395     }
1396     
1397 }