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