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