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 */ 23package com.sun.org.apache.xml.internal.security.signature; 24 25import java.io.ByteArrayInputStream; 26import java.io.ByteArrayOutputStream; 27import java.io.IOException; 28import java.io.InputStream; 29import java.io.OutputStream; 30import java.util.ArrayList; 31import java.util.LinkedHashSet; 32import java.util.List; 33import java.util.Set; 34 35import javax.xml.XMLConstants; 36import javax.xml.parsers.DocumentBuilder; 37import javax.xml.parsers.DocumentBuilderFactory; 38import javax.xml.parsers.ParserConfigurationException; 39 40import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; 41import com.sun.org.apache.xml.internal.security.c14n.implementations.CanonicalizerBase; 42import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315OmitComments; 43import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer11_OmitComments; 44import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityRuntimeException; 45import com.sun.org.apache.xml.internal.security.utils.JavaUtils; 46import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 47import org.w3c.dom.Document; 48import org.w3c.dom.Node; 49import org.xml.sax.SAXException; 50 51/** 52 * Class XMLSignatureInput 53 * 54 * @author Christian Geuer-Pollmann 55 * $todo$ check whether an XMLSignatureInput can be _both_, octet stream _and_ node set? 56 */ 57public class XMLSignatureInput { 58 /* 59 * The XMLSignature Input can be either: 60 * A byteArray like with/or without InputStream. 61 * Or a nodeSet like defined either: 62 * * as a collection of nodes 63 * * or as subnode excluding or not comments and excluding or 64 * not other nodes. 65 */ 66 67 /** 68 * Some InputStreams do not support the {@link java.io.InputStream#reset} 69 * method, so we read it in completely and work on our Proxy. 70 */ 71 private InputStream inputOctetStreamProxy = null; 72 /** 73 * The original NodeSet for this XMLSignatureInput 74 */ 75 private Set<Node> inputNodeSet = null; 76 /** 77 * The original Element 78 */ 79 private Node subNode = null; 80 /** 81 * Exclude Node *for enveloped transformations* 82 */ 83 private Node excludeNode = null; 84 /** 85 * 86 */ 87 private boolean excludeComments = false; 88 89 private boolean isNodeSet = false; 90 /** 91 * A cached bytes 92 */ 93 private byte[] bytes = null; 94 95 /** 96 * Some Transforms may require explicit MIME type, charset (IANA registered 97 * "character set"), or other such information concerning the data they are 98 * receiving from an earlier Transform or the source data, although no 99 * Transform algorithm specified in this document needs such explicit 100 * information. Such data characteristics are provided as parameters to the 101 * Transform algorithm and should be described in the specification for the 102 * algorithm. 103 */ 104 private String mimeType = null; 105 106 /** 107 * Field sourceURI 108 */ 109 private String sourceURI = null; 110 111 /** 112 * Node Filter list. 113 */ 114 private List<NodeFilter> nodeFilters = new ArrayList<NodeFilter>(); 115 116 private boolean needsToBeExpanded = false; 117 private OutputStream outputStream = null; 118 119 private DocumentBuilderFactory dfactory; 120 121 /** 122 * Construct a XMLSignatureInput from an octet array. 123 * <p> 124 * This is a comfort method, which internally converts the byte[] array into 125 * an InputStream 126 * <p>NOTE: no defensive copy</p> 127 * @param inputOctets an octet array which including XML document or node 128 */ 129 public XMLSignatureInput(byte[] inputOctets) { 130 // NO defensive copy 131 this.bytes = inputOctets; 132 } 133 134 /** 135 * Constructs a <code>XMLSignatureInput</code> from an octet stream. The 136 * stream is directly read. 137 * 138 * @param inputOctetStream 139 */ 140 public XMLSignatureInput(InputStream inputOctetStream) { 141 this.inputOctetStreamProxy = inputOctetStream; 142 } 143 144 /** 145 * Construct a XMLSignatureInput from a subtree rooted by rootNode. This 146 * method included the node and <I>all</I> his descendants in the output. 147 * 148 * @param rootNode 149 */ 150 public XMLSignatureInput(Node rootNode) { 151 this.subNode = rootNode; 152 } 153 154 /** 155 * Constructor XMLSignatureInput 156 * 157 * @param inputNodeSet 158 */ 159 public XMLSignatureInput(Set<Node> inputNodeSet) { 160 this.inputNodeSet = inputNodeSet; 161 } 162 163 /** 164 * Check if the structure needs to be expanded. 165 * @return true if so. 166 */ 167 public boolean isNeedsToBeExpanded() { 168 return needsToBeExpanded; 169 } 170 171 /** 172 * Set if the structure needs to be expanded. 173 * @param needsToBeExpanded true if so. 174 */ 175 public void setNeedsToBeExpanded(boolean needsToBeExpanded) { 176 this.needsToBeExpanded = needsToBeExpanded; 177 } 178 179 /** 180 * Returns the node set from input which was specified as the parameter of 181 * {@link XMLSignatureInput} constructor 182 * 183 * @return the node set 184 * @throws SAXException 185 * @throws IOException 186 * @throws ParserConfigurationException 187 * @throws CanonicalizationException 188 */ 189 public Set<Node> getNodeSet() throws CanonicalizationException, ParserConfigurationException, 190 IOException, SAXException { 191 return getNodeSet(false); 192 } 193 194 /** 195 * Get the Input NodeSet. 196 * @return the Input NodeSet. 197 */ 198 public Set<Node> getInputNodeSet() { 199 return inputNodeSet; 200 } 201 202 /** 203 * Returns the node set from input which was specified as the parameter of 204 * {@link XMLSignatureInput} constructor 205 * @param circumvent 206 * 207 * @return the node set 208 * @throws SAXException 209 * @throws IOException 210 * @throws ParserConfigurationException 211 * @throws CanonicalizationException 212 */ 213 public Set<Node> getNodeSet(boolean circumvent) throws ParserConfigurationException, 214 IOException, SAXException, CanonicalizationException { 215 if (inputNodeSet != null) { 216 return inputNodeSet; 217 } 218 if (inputOctetStreamProxy == null && subNode != null) { 219 if (circumvent) { 220 XMLUtils.circumventBug2650(XMLUtils.getOwnerDocument(subNode)); 221 } 222 inputNodeSet = new LinkedHashSet<Node>(); 223 XMLUtils.getSet(subNode, inputNodeSet, excludeNode, excludeComments); 224 return inputNodeSet; 225 } else if (isOctetStream()) { 226 convertToNodes(); 227 Set<Node> result = new LinkedHashSet<Node>(); 228 XMLUtils.getSet(subNode, result, null, false); 229 return result; 230 } 231 232 throw new RuntimeException("getNodeSet() called but no input data present"); 233 } 234 235 /** 236 * Returns the Octet stream(byte Stream) from input which was specified as 237 * the parameter of {@link XMLSignatureInput} constructor 238 * 239 * @return the Octet stream(byte Stream) from input which was specified as 240 * the parameter of {@link XMLSignatureInput} constructor 241 * @throws IOException 242 */ 243 public InputStream getOctetStream() throws IOException { 244 if (inputOctetStreamProxy != null) { 245 return inputOctetStreamProxy; 246 } 247 248 if (bytes != null) { 249 inputOctetStreamProxy = new ByteArrayInputStream(bytes); 250 return inputOctetStreamProxy; 251 } 252 253 return null; 254 } 255 256 /** 257 * @return real octet stream 258 */ 259 public InputStream getOctetStreamReal() { 260 return inputOctetStreamProxy; 261 } 262 263 /** 264 * Returns the byte array from input which was specified as the parameter of 265 * {@link XMLSignatureInput} constructor 266 * 267 * @return the byte[] from input which was specified as the parameter of 268 * {@link XMLSignatureInput} constructor 269 * 270 * @throws CanonicalizationException 271 * @throws IOException 272 */ 273 public byte[] getBytes() throws IOException, CanonicalizationException { 274 byte[] inputBytes = getBytesFromInputStream(); 275 if (inputBytes != null) { 276 return inputBytes; 277 } 278 Canonicalizer20010315OmitComments c14nizer = new Canonicalizer20010315OmitComments(); 279 bytes = c14nizer.engineCanonicalize(this); 280 return bytes; 281 } 282 283 /** 284 * Determines if the object has been set up with a Node set 285 * 286 * @return true if the object has been set up with a Node set 287 */ 288 public boolean isNodeSet() { 289 return ((inputOctetStreamProxy == null 290 && inputNodeSet != null) || isNodeSet); 291 } 292 293 /** 294 * Determines if the object has been set up with an Element 295 * 296 * @return true if the object has been set up with an Element 297 */ 298 public boolean isElement() { 299 return (inputOctetStreamProxy == null && subNode != null 300 && inputNodeSet == null && !isNodeSet); 301 } 302 303 /** 304 * Determines if the object has been set up with an octet stream 305 * 306 * @return true if the object has been set up with an octet stream 307 */ 308 public boolean isOctetStream() { 309 return ((inputOctetStreamProxy != null || bytes != null) 310 && (inputNodeSet == null && subNode == null)); 311 } 312 313 /** 314 * Determines if {@link #setOutputStream} has been called with a 315 * non-null OutputStream. 316 * 317 * @return true if {@link #setOutputStream} has been called with a 318 * non-null OutputStream 319 */ 320 public boolean isOutputStreamSet() { 321 return outputStream != null; 322 } 323 324 /** 325 * Determines if the object has been set up with a ByteArray 326 * 327 * @return true is the object has been set up with an octet stream 328 */ 329 public boolean isByteArray() { 330 return (bytes != null && (this.inputNodeSet == null && subNode == null)); 331 } 332 333 /** 334 * Is the object correctly set up? 335 * 336 * @return true if the object has been set up correctly 337 */ 338 public boolean isInitialized() { 339 return isOctetStream() || isNodeSet(); 340 } 341 342 /** 343 * Returns mimeType 344 * 345 * @return mimeType 346 */ 347 public String getMIMEType() { 348 return mimeType; 349 } 350 351 /** 352 * Sets mimeType 353 * 354 * @param mimeType 355 */ 356 public void setMIMEType(String mimeType) { 357 this.mimeType = mimeType; 358 } 359 360 /** 361 * Return SourceURI 362 * 363 * @return SourceURI 364 */ 365 public String getSourceURI() { 366 return sourceURI; 367 } 368 369 /** 370 * Sets SourceURI 371 * 372 * @param sourceURI 373 */ 374 public void setSourceURI(String sourceURI) { 375 this.sourceURI = sourceURI; 376 } 377 378 /** 379 * Method toString 380 * @inheritDoc 381 */ 382 public String toString() { 383 if (isNodeSet()) { 384 return "XMLSignatureInput/NodeSet/" + inputNodeSet.size() 385 + " nodes/" + getSourceURI(); 386 } 387 if (isElement()) { 388 return "XMLSignatureInput/Element/" + subNode 389 + " exclude "+ excludeNode + " comments:" 390 + excludeComments +"/" + getSourceURI(); 391 } 392 try { 393 return "XMLSignatureInput/OctetStream/" + getBytes().length 394 + " octets/" + getSourceURI(); 395 } catch (IOException iex) { 396 return "XMLSignatureInput/OctetStream//" + getSourceURI(); 397 } catch (CanonicalizationException cex) { 398 return "XMLSignatureInput/OctetStream//" + getSourceURI(); 399 } 400 } 401 402 /** 403 * Method getHTMLRepresentation 404 * 405 * @throws XMLSignatureException 406 * @return The HTML representation for this XMLSignature 407 */ 408 public String getHTMLRepresentation() throws XMLSignatureException { 409 XMLSignatureInputDebugger db = new XMLSignatureInputDebugger(this); 410 return db.getHTMLRepresentation(); 411 } 412 413 /** 414 * Method getHTMLRepresentation 415 * 416 * @param inclusiveNamespaces 417 * @throws XMLSignatureException 418 * @return The HTML representation for this XMLSignature 419 */ 420 public String getHTMLRepresentation(Set<String> inclusiveNamespaces) 421 throws XMLSignatureException { 422 XMLSignatureInputDebugger db = 423 new XMLSignatureInputDebugger(this, inclusiveNamespaces); 424 return db.getHTMLRepresentation(); 425 } 426 427 /** 428 * Gets the exclude node of this XMLSignatureInput 429 * @return Returns the excludeNode. 430 */ 431 public Node getExcludeNode() { 432 return excludeNode; 433 } 434 435 /** 436 * Sets the exclude node of this XMLSignatureInput 437 * @param excludeNode The excludeNode to set. 438 */ 439 public void setExcludeNode(Node excludeNode) { 440 this.excludeNode = excludeNode; 441 } 442 443 /** 444 * Gets the node of this XMLSignatureInput 445 * @return The excludeNode set. 446 */ 447 public Node getSubNode() { 448 return subNode; 449 } 450 451 /** 452 * @return Returns the excludeComments. 453 */ 454 public boolean isExcludeComments() { 455 return excludeComments; 456 } 457 458 /** 459 * @param excludeComments The excludeComments to set. 460 */ 461 public void setExcludeComments(boolean excludeComments) { 462 this.excludeComments = excludeComments; 463 } 464 465 /** 466 * @param diOs 467 * @throws IOException 468 * @throws CanonicalizationException 469 */ 470 public void updateOutputStream(OutputStream diOs) 471 throws CanonicalizationException, IOException { 472 updateOutputStream(diOs, false); 473 } 474 475 public void updateOutputStream(OutputStream diOs, boolean c14n11) 476 throws CanonicalizationException, IOException { 477 if (diOs == outputStream) { 478 return; 479 } 480 if (bytes != null) { 481 diOs.write(bytes); 482 } else if (inputOctetStreamProxy == null) { 483 CanonicalizerBase c14nizer = null; 484 if (c14n11) { 485 c14nizer = new Canonicalizer11_OmitComments(); 486 } else { 487 c14nizer = new Canonicalizer20010315OmitComments(); 488 } 489 c14nizer.setWriter(diOs); 490 c14nizer.engineCanonicalize(this); 491 } else { 492 byte[] buffer = new byte[4 * 1024]; 493 int bytesread = 0; 494 try { 495 while ((bytesread = inputOctetStreamProxy.read(buffer)) != -1) { 496 diOs.write(buffer, 0, bytesread); 497 } 498 } catch (IOException ex) { 499 inputOctetStreamProxy.close(); 500 throw ex; 501 } 502 } 503 } 504 505 /** 506 * @param os 507 */ 508 public void setOutputStream(OutputStream os) { 509 outputStream = os; 510 } 511 512 private byte[] getBytesFromInputStream() throws IOException { 513 if (bytes != null) { 514 return bytes; 515 } 516 if (inputOctetStreamProxy == null) { 517 return null; 518 } 519 try { 520 bytes = JavaUtils.getBytesFromStream(inputOctetStreamProxy); 521 } finally { 522 inputOctetStreamProxy.close(); 523 } 524 return bytes; 525 } 526 527 /** 528 * @param filter 529 */ 530 public void addNodeFilter(NodeFilter filter) { 531 if (isOctetStream()) { 532 try { 533 convertToNodes(); 534 } catch (Exception e) { 535 throw new XMLSecurityRuntimeException( 536 "signature.XMLSignatureInput.nodesetReference", e 537 ); 538 } 539 } 540 nodeFilters.add(filter); 541 } 542 543 /** 544 * @return the node filters 545 */ 546 public List<NodeFilter> getNodeFilters() { 547 return nodeFilters; 548 } 549 550 /** 551 * @param b 552 */ 553 public void setNodeSet(boolean b) { 554 isNodeSet = b; 555 } 556 557 void convertToNodes() throws CanonicalizationException, 558 ParserConfigurationException, IOException, SAXException { 559 if (dfactory == null) { 560 dfactory = DocumentBuilderFactory.newInstance(); 561 dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); 562 dfactory.setValidating(false); 563 dfactory.setNamespaceAware(true); 564 } 565 DocumentBuilder db = dfactory.newDocumentBuilder(); 566 // select all nodes, also the comments. 567 try { 568 db.setErrorHandler(new com.sun.org.apache.xml.internal.security.utils.IgnoreAllErrorHandler()); 569 570 Document doc = db.parse(this.getOctetStream()); 571 this.subNode = doc; 572 } catch (SAXException ex) { 573 // if a not-wellformed nodeset exists, put a container around it... 574 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 575 576 baos.write("<container>".getBytes("UTF-8")); 577 baos.write(this.getBytes()); 578 baos.write("</container>".getBytes("UTF-8")); 579 580 byte result[] = baos.toByteArray(); 581 Document document = db.parse(new ByteArrayInputStream(result)); 582 this.subNode = document.getDocumentElement().getFirstChild().getFirstChild(); 583 } finally { 584 if (this.inputOctetStreamProxy != null) { 585 this.inputOctetStreamProxy.close(); 586 } 587 this.inputOctetStreamProxy = null; 588 this.bytes = null; 589 } 590 } 591 592} 593