1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23 package com.sun.org.apache.xml.internal.security.utils; 24 25 import java.io.IOException; 26 import java.io.OutputStream; 27 import java.security.AccessController; 28 import java.security.PrivilegedAction; 29 import java.util.ArrayList; 30 import java.util.HashSet; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Set; 34 35 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; 36 import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer; 37 import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException; 38 import org.w3c.dom.Attr; 39 import org.w3c.dom.Document; 40 import org.w3c.dom.Element; 41 import org.w3c.dom.NamedNodeMap; 42 import org.w3c.dom.Node; 43 import org.w3c.dom.NodeList; 44 import org.w3c.dom.ProcessingInstruction; 45 import org.w3c.dom.Text; 46 47 /** 48 * DOM and XML accessibility and comfort functions. 49 * 50 * @author Christian Geuer-Pollmann 51 */ 52 public class XMLUtils { 53 54 private static boolean ignoreLineBreaks = 55 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 56 public Boolean run() { 57 return Boolean.valueOf(Boolean.getBoolean 58 ("com.sun.org.apache.xml.internal.security.ignoreLineBreaks")); 59 } 60 }).booleanValue(); 61 62 private static volatile String dsPrefix = "ds"; 63 private static volatile String ds11Prefix = "dsig11"; 64 private static volatile String xencPrefix = "xenc"; 65 private static volatile String xenc11Prefix = "xenc11"; 66 67 /** {@link org.apache.commons.logging} logging facility */ 68 private static final java.util.logging.Logger log = 69 java.util.logging.Logger.getLogger(XMLUtils.class.getName()); 70 71 72 /** 73 * Constructor XMLUtils 74 * 75 */ 76 private XMLUtils() { 77 // we don't allow instantiation 78 } 79 80 /** 81 * Set the prefix for the digital signature namespace 82 * @param prefix the new prefix for the digital signature namespace 83 */ 84 public static void setDsPrefix(String prefix) { 85 dsPrefix = prefix; 86 } 87 88 /** 89 * Set the prefix for the digital signature 1.1 namespace 90 * @param prefix the new prefix for the digital signature 1.1 namespace 91 */ 92 public static void setDs11Prefix(String prefix) { 93 ds11Prefix = prefix; 94 } 95 96 /** 97 * Set the prefix for the encryption namespace 98 * @param prefix the new prefix for the encryption namespace 99 */ 100 public static void setXencPrefix(String prefix) { 101 xencPrefix = prefix; 102 } 103 104 /** 105 * Set the prefix for the encryption namespace 1.1 106 * @param prefix the new prefix for the encryption namespace 1.1 107 */ 108 public static void setXenc11Prefix(String prefix) { 109 xenc11Prefix = prefix; 110 } 111 112 public static Element getNextElement(Node el) { 113 Node node = el; 114 while ((node != null) && (node.getNodeType() != Node.ELEMENT_NODE)) { 115 node = node.getNextSibling(); 116 } 117 return (Element)node; 118 } 119 120 /** 121 * @param rootNode 122 * @param result 123 * @param exclude 124 * @param com whether comments or not 125 */ 126 public static void getSet(Node rootNode, Set<Node> result, Node exclude, boolean com) { 127 if ((exclude != null) && isDescendantOrSelf(exclude, rootNode)) { 128 return; 129 } 130 getSetRec(rootNode, result, exclude, com); 131 } 132 133 @SuppressWarnings("fallthrough") 134 private static void getSetRec(final Node rootNode, final Set<Node> result, 135 final Node exclude, final boolean com) { 136 if (rootNode == exclude) { 137 return; 138 } 139 switch (rootNode.getNodeType()) { 140 case Node.ELEMENT_NODE: 141 result.add(rootNode); 142 Element el = (Element)rootNode; 143 if (el.hasAttributes()) { 144 NamedNodeMap nl = el.getAttributes(); 145 for (int i = 0;i < nl.getLength(); i++) { 146 result.add(nl.item(i)); 147 } 148 } 149 //no return keep working 150 case Node.DOCUMENT_NODE: 151 for (Node r = rootNode.getFirstChild(); r != null; r = r.getNextSibling()) { 152 if (r.getNodeType() == Node.TEXT_NODE) { 153 result.add(r); 154 while ((r != null) && (r.getNodeType() == Node.TEXT_NODE)) { 155 r = r.getNextSibling(); 156 } 157 if (r == null) { 158 return; 159 } 160 } 161 getSetRec(r, result, exclude, com); 162 } 163 return; 164 case Node.COMMENT_NODE: 165 if (com) { 166 result.add(rootNode); 167 } 168 return; 169 case Node.DOCUMENT_TYPE_NODE: 170 return; 171 default: 172 result.add(rootNode); 173 } 174 } 175 176 177 /** 178 * Outputs a DOM tree to an {@link OutputStream}. 179 * 180 * @param contextNode root node of the DOM tree 181 * @param os the {@link OutputStream} 182 */ 183 public static void outputDOM(Node contextNode, OutputStream os) { 184 XMLUtils.outputDOM(contextNode, os, false); 185 } 186 187 /** 188 * Outputs a DOM tree to an {@link OutputStream}. <I>If an Exception is 189 * thrown during execution, it's StackTrace is output to System.out, but the 190 * Exception is not re-thrown.</I> 191 * 192 * @param contextNode root node of the DOM tree 193 * @param os the {@link OutputStream} 194 * @param addPreamble 195 */ 196 public static void outputDOM(Node contextNode, OutputStream os, boolean addPreamble) { 197 try { 198 if (addPreamble) { 199 os.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".getBytes("UTF-8")); 200 } 201 202 os.write(Canonicalizer.getInstance( 203 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(contextNode) 204 ); 205 } catch (IOException ex) { 206 if (log.isLoggable(java.util.logging.Level.FINE)) { 207 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex); 208 } 209 } 210 catch (InvalidCanonicalizerException ex) { 211 if (log.isLoggable(java.util.logging.Level.FINE)) { 212 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex); 213 } 214 } catch (CanonicalizationException ex) { 215 if (log.isLoggable(java.util.logging.Level.FINE)) { 216 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex); 217 } 218 } 219 } 220 221 /** 222 * Serializes the <CODE>contextNode</CODE> into the OutputStream, <I>but 223 * suppresses all Exceptions</I>. 224 * <BR /> 225 * NOTE: <I>This should only be used for debugging purposes, 226 * NOT in a production environment; this method ignores all exceptions, 227 * so you won't notice if something goes wrong. If you're asking what is to 228 * be used in a production environment, simply use the code inside the 229 * <code>try{}</code> statement, but handle the Exceptions appropriately.</I> 230 * 231 * @param contextNode 232 * @param os 233 */ 234 public static void outputDOMc14nWithComments(Node contextNode, OutputStream os) { 235 try { 236 os.write(Canonicalizer.getInstance( 237 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(contextNode) 238 ); 239 } catch (IOException ex) { 240 if (log.isLoggable(java.util.logging.Level.FINE)) { 241 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex); 242 } 243 // throw new RuntimeException(ex.getMessage()); 244 } catch (InvalidCanonicalizerException ex) { 245 if (log.isLoggable(java.util.logging.Level.FINE)) { 246 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex); 247 } 248 // throw new RuntimeException(ex.getMessage()); 249 } catch (CanonicalizationException ex) { 250 if (log.isLoggable(java.util.logging.Level.FINE)) { 251 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex); 252 } 253 // throw new RuntimeException(ex.getMessage()); 254 } 255 } 256 257 /** 258 * Method getFullTextChildrenFromElement 259 * 260 * @param element 261 * @return the string of children 262 */ 263 public static String getFullTextChildrenFromElement(Element element) { 264 StringBuilder sb = new StringBuilder(); 265 266 Node child = element.getFirstChild(); 267 while (child != null) { 268 if (child.getNodeType() == Node.TEXT_NODE) { 269 sb.append(((Text)child).getData()); 270 } 271 child = child.getNextSibling(); 272 } 273 274 return sb.toString(); 275 } 276 277 /** 278 * Creates an Element in the XML Signature specification namespace. 279 * 280 * @param doc the factory Document 281 * @param elementName the local name of the Element 282 * @return the Element 283 */ 284 public static Element createElementInSignatureSpace(Document doc, String elementName) { 285 if (doc == null) { 286 throw new RuntimeException("Document is null"); 287 } 288 289 if ((dsPrefix == null) || (dsPrefix.length() == 0)) { 290 return doc.createElementNS(Constants.SignatureSpecNS, elementName); 291 } 292 return doc.createElementNS(Constants.SignatureSpecNS, dsPrefix + ":" + elementName); 293 } 294 295 /** 296 * Creates an Element in the XML Signature 1.1 specification namespace. 297 * 298 * @param doc the factory Document 299 * @param elementName the local name of the Element 300 * @return the Element 301 */ 302 public static Element createElementInSignature11Space(Document doc, String elementName) { 303 if (doc == null) { 304 throw new RuntimeException("Document is null"); 305 } 306 307 if ((ds11Prefix == null) || (ds11Prefix.length() == 0)) { 308 return doc.createElementNS(Constants.SignatureSpec11NS, elementName); 309 } 310 return doc.createElementNS(Constants.SignatureSpec11NS, ds11Prefix + ":" + elementName); 311 } 312 313 /** 314 * Creates an Element in the XML Encryption specification namespace. 315 * 316 * @param doc the factory Document 317 * @param elementName the local name of the Element 318 * @return the Element 319 */ 320 public static Element createElementInEncryptionSpace(Document doc, String elementName) { 321 if (doc == null) { 322 throw new RuntimeException("Document is null"); 323 } 324 325 if ((xencPrefix == null) || (xencPrefix.length() == 0)) { 326 return doc.createElementNS(EncryptionConstants.EncryptionSpecNS, elementName); 327 } 328 return 329 doc.createElementNS( 330 EncryptionConstants.EncryptionSpecNS, xencPrefix + ":" + elementName 331 ); 332 } 333 334 /** 335 * Creates an Element in the XML Encryption 1.1 specification namespace. 336 * 337 * @param doc the factory Document 338 * @param elementName the local name of the Element 339 * @return the Element 340 */ 341 public static Element createElementInEncryption11Space(Document doc, String elementName) { 342 if (doc == null) { 343 throw new RuntimeException("Document is null"); 344 } 345 346 if ((xenc11Prefix == null) || (xenc11Prefix.length() == 0)) { 347 return doc.createElementNS(EncryptionConstants.EncryptionSpec11NS, elementName); 348 } 349 return 350 doc.createElementNS( 351 EncryptionConstants.EncryptionSpec11NS, xenc11Prefix + ":" + elementName 352 ); 353 } 354 355 /** 356 * Returns true if the element is in XML Signature namespace and the local 357 * name equals the supplied one. 358 * 359 * @param element 360 * @param localName 361 * @return true if the element is in XML Signature namespace and the local name equals 362 * the supplied one 363 */ 364 public static boolean elementIsInSignatureSpace(Element element, String localName) { 365 if (element == null){ 366 return false; 367 } 368 369 return Constants.SignatureSpecNS.equals(element.getNamespaceURI()) 370 && element.getLocalName().equals(localName); 371 } 372 373 /** 374 * Returns true if the element is in XML Signature 1.1 namespace and the local 375 * name equals the supplied one. 376 * 377 * @param element 378 * @param localName 379 * @return true if the element is in XML Signature namespace and the local name equals 380 * the supplied one 381 */ 382 public static boolean elementIsInSignature11Space(Element element, String localName) { 383 if (element == null) { 384 return false; 385 } 386 387 return Constants.SignatureSpec11NS.equals(element.getNamespaceURI()) 388 && element.getLocalName().equals(localName); 389 } 390 391 /** 392 * Returns true if the element is in XML Encryption namespace and the local 393 * name equals the supplied one. 394 * 395 * @param element 396 * @param localName 397 * @return true if the element is in XML Encryption namespace and the local name 398 * equals the supplied one 399 */ 400 public static boolean elementIsInEncryptionSpace(Element element, String localName) { 401 if (element == null){ 402 return false; 403 } 404 return EncryptionConstants.EncryptionSpecNS.equals(element.getNamespaceURI()) 405 && element.getLocalName().equals(localName); 406 } 407 408 /** 409 * Returns true if the element is in XML Encryption 1.1 namespace and the local 410 * name equals the supplied one. 411 * 412 * @param element 413 * @param localName 414 * @return true if the element is in XML Encryption 1.1 namespace and the local name 415 * equals the supplied one 416 */ 417 public static boolean elementIsInEncryption11Space(Element element, String localName) { 418 if (element == null){ 419 return false; 420 } 421 return EncryptionConstants.EncryptionSpec11NS.equals(element.getNamespaceURI()) 422 && element.getLocalName().equals(localName); 423 } 424 425 /** 426 * This method returns the owner document of a particular node. 427 * This method is necessary because it <I>always</I> returns a 428 * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE> 429 * if the {@link Node} is a {@link Document}. 430 * 431 * @param node 432 * @return the owner document of the node 433 */ 434 public static Document getOwnerDocument(Node node) { 435 if (node.getNodeType() == Node.DOCUMENT_NODE) { 436 return (Document) node; 437 } 438 try { 439 return node.getOwnerDocument(); 440 } catch (NullPointerException npe) { 441 throw new NullPointerException(I18n.translate("endorsed.jdk1.4.0") 442 + " Original message was \"" 443 + npe.getMessage() + "\""); 444 } 445 } 446 447 /** 448 * This method returns the first non-null owner document of the Nodes in this Set. 449 * This method is necessary because it <I>always</I> returns a 450 * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE> 451 * if the {@link Node} is a {@link Document}. 452 * 453 * @param xpathNodeSet 454 * @return the owner document 455 */ 456 public static Document getOwnerDocument(Set<Node> xpathNodeSet) { 457 NullPointerException npe = null; 458 for (Node node : xpathNodeSet) { 459 int nodeType = node.getNodeType(); 460 if (nodeType == Node.DOCUMENT_NODE) { 461 return (Document) node; 462 } 463 try { 464 if (nodeType == Node.ATTRIBUTE_NODE) { 465 return ((Attr)node).getOwnerElement().getOwnerDocument(); 466 } 467 return node.getOwnerDocument(); 468 } catch (NullPointerException e) { 469 npe = e; 470 } 471 } 472 473 throw new NullPointerException(I18n.translate("endorsed.jdk1.4.0") 474 + " Original message was \"" 475 + (npe == null ? "" : npe.getMessage()) + "\""); 476 } 477 478 /** 479 * Method createDSctx 480 * 481 * @param doc 482 * @param prefix 483 * @param namespace 484 * @return the element. 485 */ 486 public static Element createDSctx(Document doc, String prefix, String namespace) { 487 if ((prefix == null) || (prefix.trim().length() == 0)) { 488 throw new IllegalArgumentException("You must supply a prefix"); 489 } 490 491 Element ctx = doc.createElementNS(null, "namespaceContext"); 492 493 ctx.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix.trim(), namespace); 494 495 return ctx; 496 } 497 498 /** 499 * Method addReturnToElement 500 * 501 * @param e 502 */ 503 public static void addReturnToElement(Element e) { 504 if (!ignoreLineBreaks) { 505 Document doc = e.getOwnerDocument(); 506 e.appendChild(doc.createTextNode("\n")); 507 } 508 } 509 510 public static void addReturnToElement(Document doc, HelperNodeList nl) { 511 if (!ignoreLineBreaks) { 512 nl.appendChild(doc.createTextNode("\n")); 513 } 514 } 515 516 public static void addReturnBeforeChild(Element e, Node child) { 517 if (!ignoreLineBreaks) { 518 Document doc = e.getOwnerDocument(); 519 e.insertBefore(doc.createTextNode("\n"), child); 520 } 521 } 522 523 /** 524 * Method convertNodelistToSet 525 * 526 * @param xpathNodeSet 527 * @return the set with the nodelist 528 */ 529 public static Set<Node> convertNodelistToSet(NodeList xpathNodeSet) { 530 if (xpathNodeSet == null) { 531 return new HashSet<Node>(); 532 } 533 534 int length = xpathNodeSet.getLength(); 535 Set<Node> set = new HashSet<Node>(length); 536 537 for (int i = 0; i < length; i++) { 538 set.add(xpathNodeSet.item(i)); 539 } 540 541 return set; 542 } 543 544 /** 545 * This method spreads all namespace attributes in a DOM document to their 546 * children. This is needed because the XML Signature XPath transform 547 * must evaluate the XPath against all nodes in the input, even against 548 * XPath namespace nodes. Through a bug in XalanJ2, the namespace nodes are 549 * not fully visible in the Xalan XPath model, so we have to do this by 550 * hand in DOM spaces so that the nodes become visible in XPath space. 551 * 552 * @param doc 553 * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650"> 554 * Namespace axis resolution is not XPath compliant </A> 555 */ 556 public static void circumventBug2650(Document doc) { 557 558 Element documentElement = doc.getDocumentElement(); 559 560 // if the document element has no xmlns definition, we add xmlns="" 561 Attr xmlnsAttr = 562 documentElement.getAttributeNodeNS(Constants.NamespaceSpecNS, "xmlns"); 563 564 if (xmlnsAttr == null) { 565 documentElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", ""); 566 } 567 568 XMLUtils.circumventBug2650internal(doc); 569 } 570 571 /** 572 * This is the work horse for {@link #circumventBug2650}. 573 * 574 * @param node 575 * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650"> 576 * Namespace axis resolution is not XPath compliant </A> 577 */ 578 @SuppressWarnings("fallthrough") 579 private static void circumventBug2650internal(Node node) { 580 Node parent = null; 581 Node sibling = null; 582 final String namespaceNs = Constants.NamespaceSpecNS; 583 do { 584 switch (node.getNodeType()) { 585 case Node.ELEMENT_NODE : 586 Element element = (Element) node; 587 if (!element.hasChildNodes()) { 588 break; 589 } 590 if (element.hasAttributes()) { 591 NamedNodeMap attributes = element.getAttributes(); 592 int attributesLength = attributes.getLength(); 593 594 for (Node child = element.getFirstChild(); child!=null; 595 child = child.getNextSibling()) { 596 597 if (child.getNodeType() != Node.ELEMENT_NODE) { 598 continue; 599 } 600 Element childElement = (Element) child; 601 602 for (int i = 0; i < attributesLength; i++) { 603 Attr currentAttr = (Attr) attributes.item(i); 604 if (!namespaceNs.equals(currentAttr.getNamespaceURI())) { 605 continue; 606 } 607 if (childElement.hasAttributeNS(namespaceNs, 608 currentAttr.getLocalName())) { 609 continue; 610 } 611 childElement.setAttributeNS(namespaceNs, 612 currentAttr.getName(), 613 currentAttr.getNodeValue()); 614 } 615 } 616 } 617 case Node.ENTITY_REFERENCE_NODE : 618 case Node.DOCUMENT_NODE : 619 parent = node; 620 sibling = node.getFirstChild(); 621 break; 622 } 623 while ((sibling == null) && (parent != null)) { 624 sibling = parent.getNextSibling(); 625 parent = parent.getParentNode(); 626 } 627 if (sibling == null) { 628 return; 629 } 630 631 node = sibling; 632 sibling = node.getNextSibling(); 633 } while (true); 634 } 635 636 /** 637 * @param sibling 638 * @param nodeName 639 * @param number 640 * @return nodes with the constraint 641 */ 642 public static Element selectDsNode(Node sibling, String nodeName, int number) { 643 while (sibling != null) { 644 if (Constants.SignatureSpecNS.equals(sibling.getNamespaceURI()) 645 && sibling.getLocalName().equals(nodeName)) { 646 if (number == 0){ 647 return (Element)sibling; 648 } 649 number--; 650 } 651 sibling = sibling.getNextSibling(); 652 } 653 return null; 654 } 655 656 /** 657 * @param sibling 658 * @param nodeName 659 * @param number 660 * @return nodes with the constraint 661 */ 662 public static Element selectDs11Node(Node sibling, String nodeName, int number) { 663 while (sibling != null) { 664 if (Constants.SignatureSpec11NS.equals(sibling.getNamespaceURI()) 665 && sibling.getLocalName().equals(nodeName)) { 666 if (number == 0){ 667 return (Element)sibling; 668 } 669 number--; 670 } 671 sibling = sibling.getNextSibling(); 672 } 673 return null; 674 } 675 676 /** 677 * @param sibling 678 * @param nodeName 679 * @param number 680 * @return nodes with the constrain 681 */ 682 public static Element selectXencNode(Node sibling, String nodeName, int number) { 683 while (sibling != null) { 684 if (EncryptionConstants.EncryptionSpecNS.equals(sibling.getNamespaceURI()) 685 && sibling.getLocalName().equals(nodeName)) { 686 if (number == 0){ 687 return (Element)sibling; 688 } 689 number--; 690 } 691 sibling = sibling.getNextSibling(); 692 } 693 return null; 694 } 695 696 697 /** 698 * @param sibling 699 * @param nodeName 700 * @param number 701 * @return nodes with the constrain 702 */ 703 public static Text selectDsNodeText(Node sibling, String nodeName, int number) { 704 Node n = selectDsNode(sibling,nodeName,number); 705 if (n == null) { 706 return null; 707 } 708 n = n.getFirstChild(); 709 while (n != null && n.getNodeType() != Node.TEXT_NODE) { 710 n = n.getNextSibling(); 711 } 712 return (Text)n; 713 } 714 715 /** 716 * @param sibling 717 * @param nodeName 718 * @param number 719 * @return nodes with the constrain 720 */ 721 public static Text selectDs11NodeText(Node sibling, String nodeName, int number) { 722 Node n = selectDs11Node(sibling,nodeName,number); 723 if (n == null) { 724 return null; 725 } 726 n = n.getFirstChild(); 727 while (n != null && n.getNodeType() != Node.TEXT_NODE) { 728 n = n.getNextSibling(); 729 } 730 return (Text)n; 731 } 732 733 /** 734 * @param sibling 735 * @param uri 736 * @param nodeName 737 * @param number 738 * @return nodes with the constrain 739 */ 740 public static Text selectNodeText(Node sibling, String uri, String nodeName, int number) { 741 Node n = selectNode(sibling,uri,nodeName,number); 742 if (n == null) { 743 return null; 744 } 745 n = n.getFirstChild(); 746 while (n != null && n.getNodeType() != Node.TEXT_NODE) { 747 n = n.getNextSibling(); 748 } 749 return (Text)n; 750 } 751 752 /** 753 * @param sibling 754 * @param uri 755 * @param nodeName 756 * @param number 757 * @return nodes with the constrain 758 */ 759 public static Element selectNode(Node sibling, String uri, String nodeName, int number) { 760 while (sibling != null) { 761 if (sibling.getNamespaceURI() != null && sibling.getNamespaceURI().equals(uri) 762 && sibling.getLocalName().equals(nodeName)) { 763 if (number == 0){ 764 return (Element)sibling; 765 } 766 number--; 767 } 768 sibling = sibling.getNextSibling(); 769 } 770 return null; 771 } 772 773 /** 774 * @param sibling 775 * @param nodeName 776 * @return nodes with the constrain 777 */ 778 public static Element[] selectDsNodes(Node sibling, String nodeName) { 779 return selectNodes(sibling, Constants.SignatureSpecNS, nodeName); 780 } 781 782 /** 783 * @param sibling 784 * @param nodeName 785 * @return nodes with the constrain 786 */ 787 public static Element[] selectDs11Nodes(Node sibling, String nodeName) { 788 return selectNodes(sibling, Constants.SignatureSpec11NS, nodeName); 789 } 790 791 /** 792 * @param sibling 793 * @param uri 794 * @param nodeName 795 * @return nodes with the constraint 796 */ 797 public static Element[] selectNodes(Node sibling, String uri, String nodeName) { 798 List<Element> list = new ArrayList<Element>(); 799 while (sibling != null) { 800 if (sibling.getNamespaceURI() != null && sibling.getNamespaceURI().equals(uri) 801 && sibling.getLocalName().equals(nodeName)) { 802 list.add((Element)sibling); 803 } 804 sibling = sibling.getNextSibling(); 805 } 806 return list.toArray(new Element[list.size()]); 807 } 808 809 /** 810 * @param signatureElement 811 * @param inputSet 812 * @return nodes with the constrain 813 */ 814 public static Set<Node> excludeNodeFromSet(Node signatureElement, Set<Node> inputSet) { 815 Set<Node> resultSet = new HashSet<Node>(); 816 Iterator<Node> iterator = inputSet.iterator(); 817 818 while (iterator.hasNext()) { 819 Node inputNode = iterator.next(); 820 821 if (!XMLUtils.isDescendantOrSelf(signatureElement, inputNode)) { 822 resultSet.add(inputNode); 823 } 824 } 825 return resultSet; 826 } 827 828 /** 829 * Method getStrFromNode 830 * 831 * @param xpathnode 832 * @return the string for the node. 833 */ 834 public static String getStrFromNode(Node xpathnode) { 835 if (xpathnode.getNodeType() == Node.TEXT_NODE) { 836 // we iterate over all siblings of the context node because eventually, 837 // the text is "polluted" with pi's or comments 838 StringBuilder sb = new StringBuilder(); 839 840 for (Node currentSibling = xpathnode.getParentNode().getFirstChild(); 841 currentSibling != null; 842 currentSibling = currentSibling.getNextSibling()) { 843 if (currentSibling.getNodeType() == Node.TEXT_NODE) { 844 sb.append(((Text) currentSibling).getData()); 845 } 846 } 847 848 return sb.toString(); 849 } else if (xpathnode.getNodeType() == Node.ATTRIBUTE_NODE) { 850 return ((Attr) xpathnode).getNodeValue(); 851 } else if (xpathnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 852 return ((ProcessingInstruction) xpathnode).getNodeValue(); 853 } 854 855 return null; 856 } 857 858 /** 859 * Returns true if the descendantOrSelf is on the descendant-or-self axis 860 * of the context node. 861 * 862 * @param ctx 863 * @param descendantOrSelf 864 * @return true if the node is descendant 865 */ 866 public static boolean isDescendantOrSelf(Node ctx, Node descendantOrSelf) { 867 if (ctx == descendantOrSelf) { 868 return true; 869 } 870 871 Node parent = descendantOrSelf; 872 873 while (true) { 874 if (parent == null) { 875 return false; 876 } 877 878 if (parent == ctx) { 879 return true; 880 } 881 882 if (parent.getNodeType() == Node.ATTRIBUTE_NODE) { 883 parent = ((Attr) parent).getOwnerElement(); 884 } else { 885 parent = parent.getParentNode(); 886 } 887 } 888 } 889 890 public static boolean ignoreLineBreaks() { 891 return ignoreLineBreaks; 892 } 893 894 /** 895 * Returns the attribute value for the attribute with the specified name. 896 * Returns null if there is no such attribute, or 897 * the empty string if the attribute value is empty. 898 * 899 * <p>This works around a limitation of the DOM 900 * <code>Element.getAttributeNode</code> method, which does not distinguish 901 * between an unspecified attribute and an attribute with a value of 902 * "" (it returns "" for both cases). 903 * 904 * @param elem the element containing the attribute 905 * @param name the name of the attribute 906 * @return the attribute value (may be null if unspecified) 907 */ 908 public static String getAttributeValue(Element elem, String name) { 909 Attr attr = elem.getAttributeNodeNS(null, name); 910 return (attr == null) ? null : attr.getValue(); 911 } 912 913 /** 914 * This method is a tree-search to help prevent against wrapping attacks. It checks that no 915 * two Elements have ID Attributes that match the "value" argument, if this is the case then 916 * "false" is returned. Note that a return value of "true" does not necessarily mean that 917 * a matching Element has been found, just that no wrapping attack has been detected. 918 */ 919 public static boolean protectAgainstWrappingAttack(Node startNode, String value) { 920 Node startParent = startNode.getParentNode(); 921 Node processedNode = null; 922 Element foundElement = null; 923 924 String id = value.trim(); 925 if (id.charAt(0) == '#') { 926 id = id.substring(1); 927 } 928 929 while (startNode != null) { 930 if (startNode.getNodeType() == Node.ELEMENT_NODE) { 931 Element se = (Element) startNode; 932 933 NamedNodeMap attributes = se.getAttributes(); 934 if (attributes != null) { 935 for (int i = 0; i < attributes.getLength(); i++) { 936 Attr attr = (Attr)attributes.item(i); 937 if (attr.isId() && id.equals(attr.getValue())) { 938 if (foundElement == null) { 939 // Continue searching to find duplicates 940 foundElement = attr.getOwnerElement(); 941 } else { 942 log.log(java.util.logging.Level.FINE, "Multiple elements with the same 'Id' attribute value!"); 943 return false; 944 } 945 } 946 } 947 } 948 } 949 950 processedNode = startNode; 951 startNode = startNode.getFirstChild(); 952 953 // no child, this node is done. 954 if (startNode == null) { 955 // close node processing, get sibling 956 startNode = processedNode.getNextSibling(); 957 } 958 959 // no more siblings, get parent, all children 960 // of parent are processed. 961 while (startNode == null) { 962 processedNode = processedNode.getParentNode(); 963 if (processedNode == startParent) { 964 return true; 965 } 966 // close parent node processing (processed node now) 967 startNode = processedNode.getNextSibling(); 968 } 969 } 970 return true; 971 } 972 973 /** 974 * This method is a tree-search to help prevent against wrapping attacks. It checks that no other 975 * Element than the given "knownElement" argument has an ID attribute that matches the "value" 976 * argument, which is the ID value of "knownElement". If this is the case then "false" is returned. 977 */ 978 public static boolean protectAgainstWrappingAttack( 979 Node startNode, Element knownElement, String value 980 ) { 981 Node startParent = startNode.getParentNode(); 982 Node processedNode = null; 983 984 String id = value.trim(); 985 if (id.charAt(0) == '#') { 986 id = id.substring(1); 987 } 988 989 while (startNode != null) { 990 if (startNode.getNodeType() == Node.ELEMENT_NODE) { 991 Element se = (Element) startNode; 992 993 NamedNodeMap attributes = se.getAttributes(); 994 if (attributes != null) { 995 for (int i = 0; i < attributes.getLength(); i++) { 996 Attr attr = (Attr)attributes.item(i); 997 if (attr.isId() && id.equals(attr.getValue()) && se != knownElement) { 998 log.log(java.util.logging.Level.FINE, "Multiple elements with the same 'Id' attribute value!"); 999 return false; 1000 } 1001 } 1002 } 1003 } 1004 1005 processedNode = startNode; 1006 startNode = startNode.getFirstChild(); 1007 1008 // no child, this node is done. 1009 if (startNode == null) { 1010 // close node processing, get sibling 1011 startNode = processedNode.getNextSibling(); 1012 } 1013 1014 // no more siblings, get parent, all children 1015 // of parent are processed. 1016 while (startNode == null) { 1017 processedNode = processedNode.getParentNode(); 1018 if (processedNode == startParent) { 1019 return true; 1020 } 1021 // close parent node processing (processed node now) 1022 startNode = processedNode.getNextSibling(); 1023 } 1024 } 1025 return true; 1026 } 1027 1028 }