1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.xml.security.c14n.implementations; 20 21 import java.io.ByteArrayOutputStream; 22 import java.io.IOException; 23 import java.io.OutputStream; 24 import java.io.UnsupportedEncodingException; 25 import java.util.ArrayList; 26 import java.util.HashMap; 27 import java.util.Iterator; 28 import java.util.List; 29 import java.util.ListIterator; 30 import java.util.Map; 31 import java.util.Set; 32 33 import javax.xml.parsers.DocumentBuilder; 34 import javax.xml.parsers.DocumentBuilderFactory; 35 import javax.xml.parsers.ParserConfigurationException; 36 37 import org.apache.xml.security.c14n.CanonicalizationException; 38 import org.apache.xml.security.c14n.CanonicalizerSpi; 39 import org.apache.xml.security.c14n.helper.AttrCompare; 40 import org.apache.xml.security.signature.NodeFilter; 41 import org.apache.xml.security.signature.XMLSignatureInput; 42 import org.apache.xml.security.utils.Constants; 43 import org.apache.xml.security.utils.UnsyncByteArrayOutputStream; 44 import org.apache.xml.security.utils.XMLUtils; 45 import org.w3c.dom.Attr; 46 import org.w3c.dom.Comment; 47 import org.w3c.dom.Element; 48 import org.w3c.dom.NamedNodeMap; 49 import org.w3c.dom.Node; 50 import org.w3c.dom.ProcessingInstruction; 51 import org.xml.sax.SAXException; 52 53 /** 54 * Abstract base class for canonicalization algorithms. 55 * 56 * @author Christian Geuer-Pollmann <geuerp@apache.org> 57 */ 58 public abstract class CanonicalizerBase extends CanonicalizerSpi { 59 public static final String XML = "xml"; 60 public static final String XMLNS = "xmlns"; 61 62 protected static final AttrCompare COMPARE = new AttrCompare(); 63 protected static final Attr nullNode; 64 65 private static final byte[] END_PI = {'?','>'}; 66 private static final byte[] BEGIN_PI = {'<','?'}; 67 private static final byte[] END_COMM = {'-','-','>'}; 68 private static final byte[] BEGIN_COMM = {'<','!','-','-'}; 69 private static final byte[] XA = {'&','#','x','A',';'}; 70 private static final byte[] X9 = {'&','#','x','9',';'}; 71 private static final byte[] QUOT = {'&','q','u','o','t',';'}; 72 private static final byte[] XD = {'&','#','x','D',';'}; 73 private static final byte[] GT = {'&','g','t',';'}; 74 private static final byte[] LT = {'&','l','t',';'}; 75 private static final byte[] END_TAG = {'<','/'}; 76 private static final byte[] AMP = {'&','a','m','p',';'}; 77 private static final byte[] equalsStr = {'=','\"'}; 78 79 protected static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1; 80 protected static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0; 81 protected static final int NODE_AFTER_DOCUMENT_ELEMENT = 1; 82 83 static { 84 // The null xmlns definition. 85 try { 86 DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 87 nullNode = documentBuilder.newDocument().createAttributeNS(Constants.NamespaceSpecNS, XMLNS); 88 nullNode.setValue(""); 89 } catch (Exception e) { 90 throw new RuntimeException("Unable to create nullNode: " + e); 91 } 92 } 93 94 private List<NodeFilter> nodeFilter; 95 96 private boolean includeComments; 97 private Set<Node> xpathNodeSet; 98 /** 99 * The node to be skipped/excluded from the DOM tree 100 * in subtree canonicalizations. 101 */ 102 private Node excludeNode; 103 private OutputStream writer = new ByteArrayOutputStream(); 104 105 /** 106 * Constructor CanonicalizerBase 107 * 108 * @param includeComments 109 */ CanonicalizerBase(boolean includeComments)110 public CanonicalizerBase(boolean includeComments) { 111 this.includeComments = includeComments; 112 } 113 114 /** 115 * Method engineCanonicalizeSubTree 116 * @inheritDoc 117 * @param rootNode 118 * @throws CanonicalizationException 119 */ engineCanonicalizeSubTree(Node rootNode)120 public byte[] engineCanonicalizeSubTree(Node rootNode) 121 throws CanonicalizationException { 122 return engineCanonicalizeSubTree(rootNode, (Node)null); 123 } 124 125 /** 126 * Method engineCanonicalizeXPathNodeSet 127 * @inheritDoc 128 * @param xpathNodeSet 129 * @throws CanonicalizationException 130 */ engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet)131 public byte[] engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet) 132 throws CanonicalizationException { 133 this.xpathNodeSet = xpathNodeSet; 134 return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this.xpathNodeSet)); 135 } 136 137 /** 138 * Canonicalizes a Subtree node. 139 * @param input the root of the subtree to canicalize 140 * @return The canonicalize stream. 141 * @throws CanonicalizationException 142 */ engineCanonicalize(XMLSignatureInput input)143 public byte[] engineCanonicalize(XMLSignatureInput input) throws CanonicalizationException { 144 try { 145 if (input.isExcludeComments()) { 146 includeComments = false; 147 } 148 if (input.isOctetStream()) { 149 return engineCanonicalize(input.getBytes()); 150 } 151 if (input.isElement()) { 152 return engineCanonicalizeSubTree(input.getSubNode(), input.getExcludeNode()); 153 } else if (input.isNodeSet()) { 154 nodeFilter = input.getNodeFilters(); 155 156 circumventBugIfNeeded(input); 157 158 if (input.getSubNode() != null) { 159 return engineCanonicalizeXPathNodeSetInternal(input.getSubNode()); 160 } else { 161 return engineCanonicalizeXPathNodeSet(input.getNodeSet()); 162 } 163 } 164 return null; 165 } catch (CanonicalizationException ex) { 166 throw new CanonicalizationException("empty", ex); 167 } catch (ParserConfigurationException ex) { 168 throw new CanonicalizationException("empty", ex); 169 } catch (IOException ex) { 170 throw new CanonicalizationException("empty", ex); 171 } catch (SAXException ex) { 172 throw new CanonicalizationException("empty", ex); 173 } 174 } 175 176 /** 177 * @param writer The writer to set. 178 */ setWriter(OutputStream writer)179 public void setWriter(OutputStream writer) { 180 this.writer = writer; 181 } 182 183 /** 184 * Canonicalizes a Subtree node. 185 * 186 * @param rootNode 187 * the root of the subtree to canonicalize 188 * @param excludeNode 189 * a node to be excluded from the canonicalize operation 190 * @return The canonicalize stream. 191 * @throws CanonicalizationException 192 */ engineCanonicalizeSubTree(Node rootNode, Node excludeNode)193 protected byte[] engineCanonicalizeSubTree(Node rootNode, Node excludeNode) 194 throws CanonicalizationException { 195 this.excludeNode = excludeNode; 196 try { 197 NameSpaceSymbTable ns = new NameSpaceSymbTable(); 198 int nodeLevel = NODE_BEFORE_DOCUMENT_ELEMENT; 199 if (rootNode != null && Node.ELEMENT_NODE == rootNode.getNodeType()) { 200 //Fills the nssymbtable with the definitions of the parent of the root subnode 201 getParentNameSpaces((Element)rootNode, ns); 202 nodeLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 203 } 204 this.canonicalizeSubTree(rootNode, ns, rootNode, nodeLevel); 205 this.writer.flush(); 206 if (this.writer instanceof ByteArrayOutputStream) { 207 byte[] result = ((ByteArrayOutputStream)this.writer).toByteArray(); 208 if (reset) { 209 ((ByteArrayOutputStream)this.writer).reset(); 210 } else { 211 this.writer.close(); 212 } 213 return result; 214 } else if (this.writer instanceof UnsyncByteArrayOutputStream) { 215 byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray(); 216 if (reset) { 217 ((UnsyncByteArrayOutputStream)this.writer).reset(); 218 } else { 219 this.writer.close(); 220 } 221 return result; 222 } else { 223 this.writer.close(); 224 } 225 return null; 226 227 } catch (UnsupportedEncodingException ex) { 228 throw new CanonicalizationException("empty", ex); 229 } catch (IOException ex) { 230 throw new CanonicalizationException("empty", ex); 231 } 232 } 233 234 235 /** 236 * Method canonicalizeSubTree, this function is a recursive one. 237 * 238 * @param currentNode 239 * @param ns 240 * @param endnode 241 * @throws CanonicalizationException 242 * @throws IOException 243 */ canonicalizeSubTree( Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel )244 protected final void canonicalizeSubTree( 245 Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel 246 ) throws CanonicalizationException, IOException { 247 if (isVisibleInt(currentNode) == -1) { 248 return; 249 } 250 Node sibling = null; 251 Node parentNode = null; 252 final OutputStream writer = this.writer; 253 final Node excludeNode = this.excludeNode; 254 final boolean includeComments = this.includeComments; 255 Map<String, byte[]> cache = new HashMap<String, byte[]>(); 256 do { 257 switch (currentNode.getNodeType()) { 258 259 case Node.ENTITY_NODE : 260 case Node.NOTATION_NODE : 261 case Node.ATTRIBUTE_NODE : 262 // illegal node type during traversal 263 throw new CanonicalizationException("empty"); 264 265 case Node.DOCUMENT_FRAGMENT_NODE : 266 case Node.DOCUMENT_NODE : 267 ns.outputNodePush(); 268 sibling = currentNode.getFirstChild(); 269 break; 270 271 case Node.COMMENT_NODE : 272 if (includeComments) { 273 outputCommentToWriter((Comment) currentNode, writer, documentLevel); 274 } 275 break; 276 277 case Node.PROCESSING_INSTRUCTION_NODE : 278 outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel); 279 break; 280 281 case Node.TEXT_NODE : 282 case Node.CDATA_SECTION_NODE : 283 outputTextToWriter(currentNode.getNodeValue(), writer); 284 break; 285 286 case Node.ELEMENT_NODE : 287 documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 288 if (currentNode == excludeNode) { 289 break; 290 } 291 Element currentElement = (Element)currentNode; 292 //Add a level to the nssymbtable. So latter can be pop-back. 293 ns.outputNodePush(); 294 writer.write('<'); 295 String name = currentElement.getTagName(); 296 UtfHelpper.writeByte(name, writer, cache); 297 298 Iterator<Attr> attrs = this.handleAttributesSubtree(currentElement, ns); 299 if (attrs != null) { 300 //we output all Attrs which are available 301 while (attrs.hasNext()) { 302 Attr attr = attrs.next(); 303 outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache); 304 } 305 } 306 writer.write('>'); 307 sibling = currentNode.getFirstChild(); 308 if (sibling == null) { 309 writer.write(END_TAG); 310 UtfHelpper.writeStringToUtf8(name, writer); 311 writer.write('>'); 312 //We finished with this level, pop to the previous definitions. 313 ns.outputNodePop(); 314 if (parentNode != null) { 315 sibling = currentNode.getNextSibling(); 316 } 317 } else { 318 parentNode = currentElement; 319 } 320 break; 321 322 case Node.DOCUMENT_TYPE_NODE : 323 default : 324 break; 325 } 326 while (sibling == null && parentNode != null) { 327 writer.write(END_TAG); 328 UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache); 329 writer.write('>'); 330 //We finished with this level, pop to the previous definitions. 331 ns.outputNodePop(); 332 if (parentNode == endnode) { 333 return; 334 } 335 sibling = parentNode.getNextSibling(); 336 parentNode = parentNode.getParentNode(); 337 if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) { 338 documentLevel = NODE_AFTER_DOCUMENT_ELEMENT; 339 parentNode = null; 340 } 341 } 342 if (sibling == null) { 343 return; 344 } 345 currentNode = sibling; 346 sibling = currentNode.getNextSibling(); 347 } while(true); 348 } 349 350 engineCanonicalizeXPathNodeSetInternal(Node doc)351 private byte[] engineCanonicalizeXPathNodeSetInternal(Node doc) 352 throws CanonicalizationException { 353 try { 354 this.canonicalizeXPathNodeSet(doc, doc); 355 this.writer.flush(); 356 if (this.writer instanceof ByteArrayOutputStream) { 357 byte[] sol = ((ByteArrayOutputStream)this.writer).toByteArray(); 358 if (reset) { 359 ((ByteArrayOutputStream)this.writer).reset(); 360 } else { 361 this.writer.close(); 362 } 363 return sol; 364 } else if (this.writer instanceof UnsyncByteArrayOutputStream) { 365 byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray(); 366 if (reset) { 367 ((UnsyncByteArrayOutputStream)this.writer).reset(); 368 } else { 369 this.writer.close(); 370 } 371 return result; 372 } else { 373 this.writer.close(); 374 } 375 return null; 376 } catch (UnsupportedEncodingException ex) { 377 throw new CanonicalizationException("empty", ex); 378 } catch (IOException ex) { 379 throw new CanonicalizationException("empty", ex); 380 } 381 } 382 383 /** 384 * Canonicalizes all the nodes included in the currentNode and contained in the 385 * xpathNodeSet field. 386 * 387 * @param currentNode 388 * @param endnode 389 * @throws CanonicalizationException 390 * @throws IOException 391 */ canonicalizeXPathNodeSet(Node currentNode, Node endnode)392 protected final void canonicalizeXPathNodeSet(Node currentNode, Node endnode) 393 throws CanonicalizationException, IOException { 394 if (isVisibleInt(currentNode) == -1) { 395 return; 396 } 397 boolean currentNodeIsVisible = false; 398 NameSpaceSymbTable ns = new NameSpaceSymbTable(); 399 if (currentNode != null && Node.ELEMENT_NODE == currentNode.getNodeType()) { 400 getParentNameSpaces((Element)currentNode, ns); 401 } 402 if (currentNode == null) { 403 return; 404 } 405 Node sibling = null; 406 Node parentNode = null; 407 OutputStream writer = this.writer; 408 int documentLevel = NODE_BEFORE_DOCUMENT_ELEMENT; 409 Map<String, byte[]> cache = new HashMap<String, byte[]>(); 410 do { 411 switch (currentNode.getNodeType()) { 412 413 case Node.ENTITY_NODE : 414 case Node.NOTATION_NODE : 415 case Node.ATTRIBUTE_NODE : 416 // illegal node type during traversal 417 throw new CanonicalizationException("empty"); 418 419 case Node.DOCUMENT_FRAGMENT_NODE : 420 case Node.DOCUMENT_NODE : 421 ns.outputNodePush(); 422 sibling = currentNode.getFirstChild(); 423 break; 424 425 case Node.COMMENT_NODE : 426 if (this.includeComments && (isVisibleDO(currentNode, ns.getLevel()) == 1)) { 427 outputCommentToWriter((Comment) currentNode, writer, documentLevel); 428 } 429 break; 430 431 case Node.PROCESSING_INSTRUCTION_NODE : 432 if (isVisible(currentNode)) { 433 outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel); 434 } 435 break; 436 437 case Node.TEXT_NODE : 438 case Node.CDATA_SECTION_NODE : 439 if (isVisible(currentNode)) { 440 outputTextToWriter(currentNode.getNodeValue(), writer); 441 for (Node nextSibling = currentNode.getNextSibling(); 442 (nextSibling != null) && ((nextSibling.getNodeType() == Node.TEXT_NODE) 443 || (nextSibling.getNodeType() == Node.CDATA_SECTION_NODE)); 444 nextSibling = nextSibling.getNextSibling()) { 445 outputTextToWriter(nextSibling.getNodeValue(), writer); 446 currentNode = nextSibling; 447 sibling = currentNode.getNextSibling(); 448 } 449 } 450 break; 451 452 case Node.ELEMENT_NODE : 453 documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 454 Element currentElement = (Element) currentNode; 455 //Add a level to the nssymbtable. So latter can be pop-back. 456 String name = null; 457 int i = isVisibleDO(currentNode, ns.getLevel()); 458 if (i == -1) { 459 sibling = currentNode.getNextSibling(); 460 break; 461 } 462 currentNodeIsVisible = (i == 1); 463 if (currentNodeIsVisible) { 464 ns.outputNodePush(); 465 writer.write('<'); 466 name = currentElement.getTagName(); 467 UtfHelpper.writeByte(name, writer, cache); 468 } else { 469 ns.push(); 470 } 471 472 Iterator<Attr> attrs = handleAttributes(currentElement,ns); 473 if (attrs != null) { 474 //we output all Attrs which are available 475 while (attrs.hasNext()) { 476 Attr attr = attrs.next(); 477 outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache); 478 } 479 } 480 if (currentNodeIsVisible) { 481 writer.write('>'); 482 } 483 sibling = currentNode.getFirstChild(); 484 485 if (sibling == null) { 486 if (currentNodeIsVisible) { 487 writer.write(END_TAG); 488 UtfHelpper.writeByte(name, writer, cache); 489 writer.write('>'); 490 //We finished with this level, pop to the previous definitions. 491 ns.outputNodePop(); 492 } else { 493 ns.pop(); 494 } 495 if (parentNode != null) { 496 sibling = currentNode.getNextSibling(); 497 } 498 } else { 499 parentNode = currentElement; 500 } 501 break; 502 503 case Node.DOCUMENT_TYPE_NODE : 504 default : 505 break; 506 } 507 while (sibling == null && parentNode != null) { 508 if (isVisible(parentNode)) { 509 writer.write(END_TAG); 510 UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache); 511 writer.write('>'); 512 //We finished with this level, pop to the previous definitions. 513 ns.outputNodePop(); 514 } else { 515 ns.pop(); 516 } 517 if (parentNode == endnode) { 518 return; 519 } 520 sibling = parentNode.getNextSibling(); 521 parentNode = parentNode.getParentNode(); 522 if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) { 523 parentNode = null; 524 documentLevel = NODE_AFTER_DOCUMENT_ELEMENT; 525 } 526 } 527 if (sibling == null) { 528 return; 529 } 530 currentNode = sibling; 531 sibling = currentNode.getNextSibling(); 532 } while(true); 533 } 534 isVisibleDO(Node currentNode, int level)535 protected int isVisibleDO(Node currentNode, int level) { 536 if (nodeFilter != null) { 537 Iterator<NodeFilter> it = nodeFilter.iterator(); 538 while (it.hasNext()) { 539 int i = (it.next()).isNodeIncludeDO(currentNode, level); 540 if (i != 1) { 541 return i; 542 } 543 } 544 } 545 if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) { 546 return 0; 547 } 548 return 1; 549 } 550 isVisibleInt(Node currentNode)551 protected int isVisibleInt(Node currentNode) { 552 if (nodeFilter != null) { 553 Iterator<NodeFilter> it = nodeFilter.iterator(); 554 while (it.hasNext()) { 555 int i = (it.next()).isNodeInclude(currentNode); 556 if (i != 1) { 557 return i; 558 } 559 } 560 } 561 if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) { 562 return 0; 563 } 564 return 1; 565 } 566 isVisible(Node currentNode)567 protected boolean isVisible(Node currentNode) { 568 if (nodeFilter != null) { 569 Iterator<NodeFilter> it = nodeFilter.iterator(); 570 while (it.hasNext()) { 571 if (it.next().isNodeInclude(currentNode) != 1) { 572 return false; 573 } 574 } 575 } 576 if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) { 577 return false; 578 } 579 return true; 580 } 581 handleParent(Element e, NameSpaceSymbTable ns)582 protected void handleParent(Element e, NameSpaceSymbTable ns) { 583 if (!e.hasAttributes() && e.getNamespaceURI() == null) { 584 return; 585 } 586 NamedNodeMap attrs = e.getAttributes(); 587 int attrsLength = attrs.getLength(); 588 for (int i = 0; i < attrsLength; i++) { 589 Attr attribute = (Attr) attrs.item(i); 590 String NName = attribute.getLocalName(); 591 String NValue = attribute.getNodeValue(); 592 593 if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI()) 594 && (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue))) { 595 ns.addMapping(NName, NValue, attribute); 596 } 597 } 598 if (e.getNamespaceURI() != null) { 599 String NName = e.getPrefix(); 600 String NValue = e.getNamespaceURI(); 601 String Name; 602 if (NName == null || NName.equals("")) { 603 NName = XMLNS; 604 Name = XMLNS; 605 } else { 606 Name = XMLNS + ":" + NName; 607 } 608 Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name); 609 n.setValue(NValue); 610 ns.addMapping(NName, NValue, n); 611 } 612 } 613 614 /** 615 * Adds to ns the definitions from the parent elements of el 616 * @param el 617 * @param ns 618 */ getParentNameSpaces(Element el, NameSpaceSymbTable ns)619 protected final void getParentNameSpaces(Element el, NameSpaceSymbTable ns) { 620 Node n1 = el.getParentNode(); 621 if (n1 == null || Node.ELEMENT_NODE != n1.getNodeType()) { 622 return; 623 } 624 //Obtain all the parents of the element 625 List<Element> parents = new ArrayList<Element>(); 626 Node parent = n1; 627 while (parent != null && Node.ELEMENT_NODE == parent.getNodeType()) { 628 parents.add((Element)parent); 629 parent = parent.getParentNode(); 630 } 631 //Visit them in reverse order. 632 ListIterator<Element> it = parents.listIterator(parents.size()); 633 while (it.hasPrevious()) { 634 Element ele = it.previous(); 635 handleParent(ele, ns); 636 } 637 parents.clear(); 638 Attr nsprefix; 639 if (((nsprefix = ns.getMappingWithoutRendered(XMLNS)) != null) 640 && "".equals(nsprefix.getValue())) { 641 ns.addMappingAndRender(XMLNS, "", nullNode); 642 } 643 } 644 645 /** 646 * Obtain the attributes to output for this node in XPathNodeSet c14n. 647 * 648 * @param element 649 * @param ns 650 * @return the attributes nodes to output. 651 * @throws CanonicalizationException 652 */ handleAttributes(Element element, NameSpaceSymbTable ns)653 abstract Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns) 654 throws CanonicalizationException; 655 656 /** 657 * Obtain the attributes to output for this node in a Subtree c14n. 658 * 659 * @param element 660 * @param ns 661 * @return the attributes nodes to output. 662 * @throws CanonicalizationException 663 */ handleAttributesSubtree(Element element, NameSpaceSymbTable ns)664 abstract Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns) 665 throws CanonicalizationException; 666 circumventBugIfNeeded(XMLSignatureInput input)667 abstract void circumventBugIfNeeded(XMLSignatureInput input) 668 throws CanonicalizationException, ParserConfigurationException, IOException, SAXException; 669 670 /** 671 * Outputs an Attribute to the internal Writer. 672 * 673 * The string value of the node is modified by replacing 674 * <UL> 675 * <LI>all ampersands (&) with <CODE>&amp;</CODE></LI> 676 * <LI>all open angle brackets (<) with <CODE>&lt;</CODE></LI> 677 * <LI>all quotation mark characters with <CODE>&quot;</CODE></LI> 678 * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, with character 679 * references. The character references are written in uppercase 680 * hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> is represented 681 * by the character reference <CODE>&#xD;</CODE>)</LI> 682 * </UL> 683 * 684 * @param name 685 * @param value 686 * @param writer 687 * @throws IOException 688 */ outputAttrToWriter( final String name, final String value, final OutputStream writer, final Map<String, byte[]> cache )689 protected static final void outputAttrToWriter( 690 final String name, final String value, 691 final OutputStream writer, final Map<String, byte[]> cache 692 ) throws IOException { 693 writer.write(' '); 694 UtfHelpper.writeByte(name, writer, cache); 695 writer.write(equalsStr); 696 byte[] toWrite; 697 final int length = value.length(); 698 int i = 0; 699 while (i < length) { 700 char c = value.charAt(i++); 701 702 switch (c) { 703 704 case '&' : 705 toWrite = AMP; 706 break; 707 708 case '<' : 709 toWrite = LT; 710 break; 711 712 case '"' : 713 toWrite = QUOT; 714 break; 715 716 case 0x09 : // '\t' 717 toWrite = X9; 718 break; 719 720 case 0x0A : // '\n' 721 toWrite = XA; 722 break; 723 724 case 0x0D : // '\r' 725 toWrite = XD; 726 break; 727 728 default : 729 if (c < 0x80) { 730 writer.write(c); 731 } else { 732 UtfHelpper.writeCharToUtf8(c, writer); 733 } 734 continue; 735 } 736 writer.write(toWrite); 737 } 738 739 writer.write('\"'); 740 } 741 742 /** 743 * Outputs a PI to the internal Writer. 744 * 745 * @param currentPI 746 * @param writer where to write the things 747 * @throws IOException 748 */ outputPItoWriter( ProcessingInstruction currentPI, OutputStream writer, int position )749 protected void outputPItoWriter( 750 ProcessingInstruction currentPI, OutputStream writer, int position 751 ) throws IOException { 752 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 753 writer.write('\n'); 754 } 755 writer.write(BEGIN_PI); 756 757 final String target = currentPI.getTarget(); 758 int length = target.length(); 759 760 for (int i = 0; i < length; i++) { 761 char c = target.charAt(i); 762 if (c == 0x0D) { 763 writer.write(XD); 764 } else { 765 if (c < 0x80) { 766 writer.write(c); 767 } else { 768 UtfHelpper.writeCharToUtf8(c, writer); 769 } 770 } 771 } 772 773 final String data = currentPI.getData(); 774 775 length = data.length(); 776 777 if (length > 0) { 778 writer.write(' '); 779 780 for (int i = 0; i < length; i++) { 781 char c = data.charAt(i); 782 if (c == 0x0D) { 783 writer.write(XD); 784 } else { 785 UtfHelpper.writeCharToUtf8(c, writer); 786 } 787 } 788 } 789 790 writer.write(END_PI); 791 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 792 writer.write('\n'); 793 } 794 } 795 796 /** 797 * Method outputCommentToWriter 798 * 799 * @param currentComment 800 * @param writer writer where to write the things 801 * @throws IOException 802 */ outputCommentToWriter( Comment currentComment, OutputStream writer, int position )803 protected void outputCommentToWriter( 804 Comment currentComment, OutputStream writer, int position 805 ) throws IOException { 806 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 807 writer.write('\n'); 808 } 809 writer.write(BEGIN_COMM); 810 811 final String data = currentComment.getData(); 812 final int length = data.length(); 813 814 for (int i = 0; i < length; i++) { 815 char c = data.charAt(i); 816 if (c == 0x0D) { 817 writer.write(XD); 818 } else { 819 if (c < 0x80) { 820 writer.write(c); 821 } else { 822 UtfHelpper.writeCharToUtf8(c, writer); 823 } 824 } 825 } 826 827 writer.write(END_COMM); 828 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 829 writer.write('\n'); 830 } 831 } 832 833 /** 834 * Outputs a Text of CDATA section to the internal Writer. 835 * 836 * @param text 837 * @param writer writer where to write the things 838 * @throws IOException 839 */ outputTextToWriter( final String text, final OutputStream writer )840 protected static final void outputTextToWriter( 841 final String text, final OutputStream writer 842 ) throws IOException { 843 final int length = text.length(); 844 byte[] toWrite; 845 for (int i = 0; i < length; i++) { 846 char c = text.charAt(i); 847 848 switch (c) { 849 850 case '&' : 851 toWrite = AMP; 852 break; 853 854 case '<' : 855 toWrite = LT; 856 break; 857 858 case '>' : 859 toWrite = GT; 860 break; 861 862 case 0xD : 863 toWrite = XD; 864 break; 865 866 default : 867 if (c < 0x80) { 868 writer.write(c); 869 } else { 870 UtfHelpper.writeCharToUtf8(c, writer); 871 } 872 continue; 873 } 874 writer.write(toWrite); 875 } 876 } 877 878 } 879