| |
| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "<WebSig>" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2001, Institute for |
| * Data Communications Systems, <http://www.nue.et-inf.uni-siegen.de/>. |
| * The development of this software was partly funded by the European |
| * Commission in the <WebSig> project in the ISIS Programme. |
| * For more information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.xml.security.signature; |
| |
| |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.StringWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.io.Writer; |
| import java.util.Set; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.TransformerException; |
| |
| import org.apache.xml.security.c14n.CanonicalizationException; |
| import org.apache.xml.security.c14n.Canonicalizer; |
| import org.apache.xml.security.c14n.InvalidCanonicalizerException; |
| import org.apache.xml.security.c14n.implementations.Canonicalizer20010315OmitComments; |
| import org.apache.xml.security.utils.JavaUtils; |
| import org.apache.xml.security.utils.XMLUtils; |
| import org.apache.xpath.CachedXPathAPI; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Comment; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.ProcessingInstruction; |
| import org.xml.sax.SAXException; |
| |
| |
| /** |
| * Class XMLSignatureInput |
| * |
| * @author Christian Geuer-Pollmann |
| * $todo$ check whether an XMLSignatureInput can be _both_, octet stream _and_ node set? |
| */ |
| public class XMLSignatureInput { |
| |
| /** |
| * Some InputStreams do not support the {@link java.io.InputStream#reset} |
| * method, so we read it in completely and work on our Proxy. |
| */ |
| InputStream _inputOctetStreamProxy = null; |
| |
| /** |
| * The original NodeSet for this XMLSignatureInput |
| */ |
| Set _inputNodeSet = null; |
| |
| /** Field _cxpathAPI */ |
| CachedXPathAPI _cxpathAPI; |
| |
| /** |
| * Construct a XMLSignatureInput from an octet array. |
| * <p> |
| * This is a comfort method, which internally converts the byte[] array into an InputStream |
| * |
| * @param inputOctets an octet array which including XML document or node |
| */ |
| public XMLSignatureInput(byte[] inputOctets) { |
| |
| // defensive copy |
| byte[] copy = new byte[inputOctets.length]; |
| |
| System.arraycopy(inputOctets, 0, copy, 0, inputOctets.length); |
| |
| this._inputOctetStreamProxy = new ByteArrayInputStream(copy); |
| this._cxpathAPI = new CachedXPathAPI(); |
| } |
| |
| /** |
| * Constructs a <code>XMLSignatureInput</code> from an octet stream. The |
| * stream is directly read. |
| * |
| * @param inputOctetStream |
| * @throws IOException |
| */ |
| public XMLSignatureInput(InputStream inputOctetStream) throws IOException { |
| |
| this(JavaUtils.getBytesFromStream(inputOctetStream)); |
| |
| inputOctetStream = null; // free object reference |
| } |
| |
| /** |
| * Construct a XMLSignatureInput from a String. |
| * <p> |
| * This is a comfort method, which internally converts the String into a byte[] array using the {@link java.lang.String#getBytes} method. |
| * |
| * @param inputStr the input String which including XML document or node |
| */ |
| public XMLSignatureInput(String inputStr) { |
| this(inputStr.getBytes()); |
| } |
| |
| /** |
| * Construct a XMLSignatureInput from a String with a given encoding. |
| * <p> |
| * This is a comfort method, which internally converts the String into a byte[] array using the {@link java.lang.String#getBytes} method. |
| * |
| * @param inputStr the input String with encoding <code>encoding</code> |
| * @param encoding the encoding of <code>inputStr</code> |
| * @throws UnsupportedEncodingException |
| */ |
| public XMLSignatureInput(String inputStr, String encoding) |
| throws UnsupportedEncodingException { |
| this(inputStr.getBytes(encoding)); |
| } |
| |
| /** |
| * Construct a XMLSignatureInput from a subtree rooted by rootNode. This |
| * method included the node and <I>all</I> his descendants in the output. |
| * |
| * @param rootNode |
| * @param usedXPathAPI |
| * @throws TransformerException |
| */ |
| public XMLSignatureInput(Node rootNode, CachedXPathAPI usedXPathAPI) |
| throws TransformerException { |
| |
| this._cxpathAPI = usedXPathAPI; |
| |
| // get the Document and make all namespace nodes visible in DOM space |
| Document doc = XMLUtils.getOwnerDocument(rootNode); |
| |
| XMLUtils.circumventBug2650(doc); |
| |
| NodeList result = this._cxpathAPI.selectNodeList(rootNode, |
| Canonicalizer.XPATH_C14N_WITH_COMMENTS_SINGLE_NODE); |
| |
| this._inputNodeSet = XMLUtils.convertNodelistToSet(result); |
| } |
| |
| /** |
| * Construct a XMLSignatureInput from a subtree rooted by rootNode. This method included the node |
| * and <I>all</I> his descendants in the output. |
| * |
| * @param rootNode |
| * @throws TransformerException |
| */ |
| public XMLSignatureInput(Node rootNode) throws TransformerException { |
| this(rootNode, new CachedXPathAPI()); |
| } |
| |
| /** |
| * Constructor XMLSignatureInput |
| * |
| * @param inputNodeSet |
| * @param usedXPathAPI |
| */ |
| public XMLSignatureInput(Set inputNodeSet, CachedXPathAPI usedXPathAPI) { |
| this._inputNodeSet = inputNodeSet; |
| this._cxpathAPI = usedXPathAPI; |
| } |
| |
| /** |
| * Constructor XMLSignatureInput |
| * |
| * @param inputNodeSet |
| */ |
| public XMLSignatureInput(Set inputNodeSet) { |
| this(inputNodeSet, new CachedXPathAPI()); |
| } |
| |
| /** |
| * Constructor XMLSignatureInput |
| * |
| * @param inputNodeSet |
| * @deprecated Use {@link Set}s instead of {@link NodeList}s. |
| */ |
| public XMLSignatureInput(NodeList inputNodeSet) { |
| this(XMLUtils.convertNodelistToSet(inputNodeSet), new CachedXPathAPI()); |
| } |
| |
| /** |
| * Constructor XMLSignatureInput |
| * |
| * @param inputNodeSet |
| * @param usedXPathAPI |
| * @deprecated Use {@link Set}s instead of {@link NodeList}s. |
| */ |
| public XMLSignatureInput(NodeList inputNodeSet, |
| CachedXPathAPI usedXPathAPI) { |
| this(XMLUtils.convertNodelistToSet(inputNodeSet), usedXPathAPI); |
| } |
| |
| /** |
| * Returns the node set from input which was specified as the parameter of {@link XMLSignatureInput} constructor |
| * |
| * @return the node set |
| * @throws CanonicalizationException |
| * @throws IOException |
| * @throws InvalidCanonicalizerException |
| * @throws ParserConfigurationException |
| * @throws SAXException |
| */ |
| public Set getNodeSet() |
| throws ParserConfigurationException, IOException, SAXException, |
| CanonicalizationException, InvalidCanonicalizerException { |
| |
| if (this.isNodeSet()) { |
| return this._inputNodeSet; |
| } else if (this.isOctetStream()) { |
| DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); |
| |
| dfactory.setValidating(false); |
| dfactory.setNamespaceAware(true); |
| |
| DocumentBuilder db = dfactory.newDocumentBuilder(); |
| |
| try { |
| db.setErrorHandler(new org.apache.xml.security.utils |
| .IgnoreAllErrorHandler()); |
| |
| Document doc = db.parse(this.getOctetStream()); |
| |
| XMLUtils.circumventBug2650(doc); |
| |
| // select all nodes, also the comments. |
| NodeList nodeList = |
| this._cxpathAPI |
| .selectNodeList(doc, |
| Canonicalizer |
| .XPATH_C14N_WITH_COMMENTS_SINGLE_NODE); |
| |
| return XMLUtils.convertNodelistToSet(nodeList); |
| } catch (TransformerException ex) { |
| throw new CanonicalizationException("generic.EmptyMessage", ex); |
| } catch (SAXException ex) { |
| |
| // if a not-wellformed nodeset exists, put a container around it... |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| baos.write("<container>".getBytes()); |
| baos.write(this.getBytes()); |
| baos.write("</container>".getBytes()); |
| |
| byte result[] = baos.toByteArray(); |
| Document document = db.parse(new ByteArrayInputStream(result)); |
| |
| XMLUtils.circumventBug2650(document); |
| |
| try { |
| NodeList nodeList = this._cxpathAPI.selectNodeList( |
| document, |
| "(//. | //@* | //namespace::*)[not(self::node()=/) and not(self::node=/container)]"); |
| |
| return XMLUtils.convertNodelistToSet(nodeList); |
| } catch (TransformerException ex2) { |
| throw new CanonicalizationException("generic.EmptyMessage", ex2); |
| } |
| } |
| } |
| |
| throw new RuntimeException( |
| "getNodeSet() called but no input data present"); |
| } |
| |
| /** |
| * Returns the Octect stream(byte Stream) from input which was specified as the parameter of {@link XMLSignatureInput} constructor |
| * |
| * @return the Octect stream(byte Stream) from input which was specified as the parameter of {@link XMLSignatureInput} constructor |
| * @throws CanonicalizationException |
| * @throws IOException |
| * @throws InvalidCanonicalizerException |
| */ |
| public InputStream getOctetStream() |
| throws IOException, CanonicalizationException, |
| InvalidCanonicalizerException { |
| |
| if (this.isOctetStream()) { |
| this._inputOctetStreamProxy.reset(); |
| |
| return this._inputOctetStreamProxy; |
| } else if (this.isNodeSet()) { |
| |
| /* If we have a node set but an octet stream is needed, we MUST c14nize |
| * without any comments. |
| * |
| * We don't use the factory because direct instantiation should be a |
| * little bit faster... |
| */ |
| Canonicalizer20010315OmitComments c14nizer = |
| new Canonicalizer20010315OmitComments(); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| if (this._inputNodeSet.size() == 0) { |
| |
| // empty nodeset |
| return new ByteArrayInputStream(baos.toByteArray()); |
| } |
| |
| try { |
| Set nodes = this.getNodeSet(); |
| byte bytes[] = c14nizer.engineCanonicalizeXPathNodeSet(nodes); |
| |
| baos.write(bytes); |
| |
| /** $todo$ Clarify behavior. If isNodeSet() and we getOctetStream, do we have to this._inputOctetStream=xxx ? */ |
| |
| /* |
| this._inputOctetStream = new ByteArrayInputStream(baos.toByteArray()); |
| this._inputNodeSet = null; |
| return this._inputOctetStream; |
| */ |
| return new ByteArrayInputStream(baos.toByteArray()); |
| } catch (SAXException ex) { |
| throw new CanonicalizationException("empty", ex); |
| } catch (ParserConfigurationException ex) { |
| throw new CanonicalizationException("empty", ex); |
| } |
| } |
| |
| throw new RuntimeException( |
| "getOctetStream() called but no input data present"); |
| } |
| |
| /** |
| * Returns the byte array from input which was specified as the parameter of {@link XMLSignatureInput} constructor |
| * |
| * @return the byte[] from input which was specified as the parameter of {@link XMLSignatureInput} constructor |
| * |
| * @throws CanonicalizationException |
| * @throws IOException |
| * @throws InvalidCanonicalizerException |
| */ |
| public byte[] getBytes() |
| throws IOException, CanonicalizationException, |
| InvalidCanonicalizerException { |
| |
| InputStream is = this.getOctetStream(); |
| int available = is.available(); |
| byte[] data = new byte[available]; |
| |
| is.read(data); |
| |
| if (available != data.length) { |
| throw new IOException("Not enough bytes read"); |
| } |
| |
| return data; |
| } |
| |
| /** |
| * Determines if the object has been set up with a Node set |
| * |
| * @return true is the object has been set up with a Node set |
| */ |
| public boolean isNodeSet() { |
| return ((this._inputOctetStreamProxy == null) |
| && (this._inputNodeSet != null)); |
| } |
| |
| /** |
| * Determines if the object has been set up with an octet stream |
| * |
| * @return true is the object has been set up with an octet stream |
| */ |
| public boolean isOctetStream() { |
| return ((this._inputOctetStreamProxy != null) |
| && (this._inputNodeSet == null)); |
| } |
| |
| /** |
| * Is the object correctly set up? |
| * |
| * @return true if the object has been set up correctly |
| */ |
| public boolean isInitialized() { |
| return (this.isOctetStream() || this.isNodeSet()); |
| } |
| |
| /** |
| * Some Transforms may require explicit MIME type, charset (IANA registered |
| * "character set"), or other such information concerning the data they |
| * are receiving from an earlier Transform or the source data, although no |
| * Transform algorithm specified in this document needs such explicit |
| * information. Such data characteristics are provided as parameters to the |
| * Transform algorithm and should be described in the specification for the |
| * algorithm. |
| */ |
| private String _MIMEType = null; |
| |
| /** |
| * Returns MIMEType |
| * |
| * @return MIMEType |
| */ |
| public String getMIMEType() { |
| return this._MIMEType; |
| } |
| |
| /** |
| * Sets MIMEType |
| * |
| * @param MIMEType |
| */ |
| public void setMIMEType(String MIMEType) { |
| this._MIMEType = MIMEType; |
| } |
| |
| /** Field _SourceURI */ |
| private String _SourceURI = null; |
| |
| /** |
| * Return SourceURI |
| * |
| * @return SourceURI |
| */ |
| public String getSourceURI() { |
| return this._SourceURI; |
| } |
| |
| /** |
| * Sets SourceURI |
| * |
| * @param SourceURI |
| */ |
| public void setSourceURI(String SourceURI) { |
| this._SourceURI = SourceURI; |
| } |
| |
| /** |
| * This method gives access to an {@link org.apache.xpath.CachedXPathAPI} |
| * object which was used for creating the internal node set and which MUST be |
| * used for subsequent operations on this node set. |
| * |
| * @return an existing {@link org.apache.xpath.CachedXPathAPI} |
| */ |
| public CachedXPathAPI getCachedXPathAPI() { |
| return this._cxpathAPI; |
| } |
| |
| /** |
| * Method toString |
| * |
| * |
| */ |
| public String toString() { |
| |
| if (this.isNodeSet()) { |
| try { |
| return "XMLSignatureInput/NodeSet/" + this._inputNodeSet.size() |
| + " nodes/" + this.getSourceURI(); |
| } catch (Exception ex) { |
| return "XMLSignatureInput/NodeSet//" + this.getSourceURI(); |
| } |
| } else { |
| try { |
| return "XMLSignatureInput/OctetStream/" + this.getBytes().length |
| + " octets/" + this.getSourceURI(); |
| } catch (Exception ex) { |
| return "XMLSignatureInput/OctetStream//" + this.getSourceURI(); |
| } |
| } |
| } |
| |
| /** |
| * Method getHTMLRepresentation |
| * |
| * @throws XMLSignatureException |
| */ |
| public String getHTMLRepresentation() throws XMLSignatureException { |
| |
| XMLSignatureInputDebugger db = new XMLSignatureInputDebugger(this); |
| |
| return db.getHTMLRepresentation(); |
| } |
| |
| /** |
| * Method getHTMLRepresentation |
| * |
| * @param inclusiveNamespaces |
| * @throws XMLSignatureException |
| */ |
| public String getHTMLRepresentation(Set inclusiveNamespaces) |
| throws XMLSignatureException { |
| |
| XMLSignatureInputDebugger db = new XMLSignatureInputDebugger(this, |
| inclusiveNamespaces); |
| |
| return db.getHTMLRepresentation(); |
| } |
| |
| /** |
| * Class XMLSignatureInputDebugger |
| * |
| * @author $Author$ |
| * @version $Revision$ |
| */ |
| public class XMLSignatureInputDebugger { |
| |
| /** Field _xmlSignatureInput */ |
| private Set _xpathNodeSet; |
| private Set _inclusiveNamespaces; |
| |
| /** Field _doc */ |
| private Document _doc = null; |
| |
| /** Field _documentElement */ |
| private Element _documentElement = null; |
| |
| /** Field _writer */ |
| private Writer _writer = null; |
| //J- |
| // public static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"><html><head><style type=\"text/css\"><!-- .INCLUDED { color: #000000; background-color: #FFFFFF; font-weight: bold; } .EXCLUDED { color: #666666; background-color: #999999; } .INCLUDEDINCLUSIVENAMESPACE { color: #0000FF; background-color: #FFFFFF; font-weight: bold; font-style: italic; } .EXCLUDEDINCLUSIVENAMESPACE { color: #0000FF; background-color: #999999; font-style: italic; } --> </style> </head><body bgcolor=\"#999999\"><pre>"; |
| public static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" |
| + "<html>\n" |
| + "<head>\n" |
| + "<title>Caninical XML node set</title>\n" |
| + "<style type=\"text/css\">\n" |
| + "<!-- \n" |
| + ".INCLUDED { \n" |
| + " color: #000000; \n" |
| + " background-color: \n" |
| + " #FFFFFF; \n" |
| + " font-weight: bold; } \n" |
| + ".EXCLUDED { \n" |
| + " color: #666666; \n" |
| + " background-color: \n" |
| + " #999999; } \n" |
| + ".INCLUDEDINCLUSIVENAMESPACE { \n" |
| + " color: #0000FF; \n" |
| + " background-color: #FFFFFF; \n" |
| + " font-weight: bold; \n" |
| + " font-style: italic; } \n" |
| + ".EXCLUDEDINCLUSIVENAMESPACE { \n" |
| + " color: #0000FF; \n" |
| + " background-color: #999999; \n" |
| + " font-style: italic; } \n" |
| + "--> \n" |
| + "</style> \n" |
| + "</head>\n" |
| + "<body bgcolor=\"#999999\">\n" |
| + "<h1>Explanation of the output</h1>\n" |
| + "<p>The following text contains the nodeset of the given Reference before it is canonicalized. There exist four different styles to indicate how a given node is treated.</p>\n" |
| + "<ul>\n" |
| + "<li class=\"INCLUDED\">A node which is in the node set is labeled using the INCLUDED style.</li>\n" |
| + "<li class=\"EXCLUDED\">A node which is <em>NOT</em> in the node set is labeled EXCLUDED style.</li>\n" |
| + "<li class=\"INCLUDEDINCLUSIVENAMESPACE\">A namespace which is in the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n" |
| + "<li class=\"EXCLUDEDINCLUSIVENAMESPACE\">A namespace which is in NOT the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n" |
| + "</ul>\n" |
| + "<h1>Output</h1>\n" |
| + "<pre>\n" ; |
| |
| |
| public static final String HTMLSuffix = "</pre></body></html>"; |
| |
| public static final String HTMLExcludePrefix = "<span class=\"EXCLUDED\">"; |
| public static final String HTMLExcludeSuffix = "</span>"; |
| |
| public static final String HTMLIncludePrefix = "<span class=\"INCLUDED\">"; |
| public static final String HTMLIncludeSuffix = "</span>"; |
| |
| public static final String HTMLIncludedInclusiveNamespacePrefix = "<span class=\"INCLUDEDINCLUSIVENAMESPACE\">"; |
| public static final String HTMLIncludedInclusiveNamespaceSuffix = "</span>"; |
| |
| public static final String HTMLExcludedInclusiveNamespacePrefix = "<span class=\"EXCLUDEDINCLUSIVENAMESPACE\">"; |
| public static final String HTMLExcludedInclusiveNamespaceSuffix = "</span>"; |
| |
| private static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1; |
| private static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0; |
| private static final int NODE_AFTER_DOCUMENT_ELEMENT = 1; |
| //J+ |
| private XMLSignatureInputDebugger() { |
| ; |
| } |
| |
| /** |
| * Constructor XMLSignatureInputDebugger |
| * |
| * @param xmlSignatureInput |
| */ |
| public XMLSignatureInputDebugger(XMLSignatureInput xmlSignatureInput) |
| throws XMLSignatureException { |
| |
| if (!xmlSignatureInput.isNodeSet()) { |
| this._xpathNodeSet = null; |
| } else { |
| this._xpathNodeSet = xmlSignatureInput._inputNodeSet; |
| } |
| } |
| |
| /** |
| * Constructor XMLSignatureInputDebugger |
| * |
| * @param xmlSignatureInput |
| * @param inclusiveNamespace |
| * @throws XMLSignatureException |
| */ |
| public XMLSignatureInputDebugger( |
| XMLSignatureInput xmlSignatureInput, Set inclusiveNamespace) |
| throws XMLSignatureException { |
| |
| this(xmlSignatureInput); |
| |
| this._inclusiveNamespaces = inclusiveNamespace; |
| } |
| |
| /** |
| * Method getHTMLRepresentation |
| * |
| */ |
| public String getHTMLRepresentation() throws XMLSignatureException { |
| |
| if ((this._xpathNodeSet == null) || (this._xpathNodeSet.size() == 0)) { |
| return HTMLPrefix + "<blink>no node set, sorry</blink>" |
| + HTMLSuffix; |
| } |
| |
| { |
| |
| // get only a single node as anchor to fetch the owner document |
| Node n = (Node) this._xpathNodeSet.iterator().next(); |
| |
| this._doc = XMLUtils.getOwnerDocument(n); |
| this._documentElement = this._doc.getDocumentElement(); |
| } |
| |
| try { |
| this._writer = new StringWriter(); |
| |
| this.canonicalizeXPathNodeSet(this._doc); |
| this._writer.close(); |
| |
| return this._writer.toString(); |
| } catch (IOException ex) { |
| throw new XMLSignatureException("empty", ex); |
| } finally { |
| this._xpathNodeSet = null; |
| this._doc = null; |
| this._documentElement = null; |
| this._writer = null; |
| } |
| } |
| |
| /** |
| * Method canonicalizeXPathNodeSet |
| * |
| * @param currentNode |
| * @throws XMLSignatureException |
| */ |
| private void canonicalizeXPathNodeSet(Node currentNode) |
| throws XMLSignatureException, IOException { |
| |
| int currentNodeType = currentNode.getNodeType(); |
| boolean currentNodeIsVisible = |
| this._xpathNodeSet.contains(currentNode); |
| |
| switch (currentNodeType) { |
| |
| case Node.DOCUMENT_TYPE_NODE : |
| default : |
| break; |
| |
| case Node.ENTITY_NODE : |
| case Node.NOTATION_NODE : |
| case Node.DOCUMENT_FRAGMENT_NODE : |
| case Node.ATTRIBUTE_NODE : |
| throw new XMLSignatureException("empty"); |
| case Node.DOCUMENT_NODE : |
| this._writer.write(HTMLPrefix); |
| |
| for (Node currentChild = currentNode.getFirstChild(); |
| currentChild != null; |
| currentChild = currentChild.getNextSibling()) { |
| this.canonicalizeXPathNodeSet(currentChild); |
| } |
| |
| this._writer.write(HTMLSuffix); |
| break; |
| |
| case Node.COMMENT_NODE : |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludePrefix); |
| } else { |
| this._writer.write(HTMLExcludePrefix); |
| } |
| |
| int position = getPositionRelativeToDocumentElement(currentNode); |
| |
| if (position == NODE_AFTER_DOCUMENT_ELEMENT) { |
| this._writer.write("\n"); |
| } |
| |
| this.outputCommentToWriter((Comment) currentNode); |
| |
| if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { |
| this._writer.write("\n"); |
| } |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludeSuffix); |
| } else { |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| break; |
| |
| case Node.PROCESSING_INSTRUCTION_NODE : |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludePrefix); |
| } else { |
| this._writer.write(HTMLExcludePrefix); |
| } |
| |
| position = getPositionRelativeToDocumentElement(currentNode); |
| |
| if (position == NODE_AFTER_DOCUMENT_ELEMENT) { |
| this._writer.write("\n"); |
| } |
| |
| this.outputPItoWriter((ProcessingInstruction) currentNode); |
| |
| if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { |
| this._writer.write("\n"); |
| } |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludeSuffix); |
| } else { |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| break; |
| |
| case Node.TEXT_NODE : |
| case Node.CDATA_SECTION_NODE : |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludePrefix); |
| } else { |
| this._writer.write(HTMLExcludePrefix); |
| } |
| |
| outputTextToWriter(currentNode.getNodeValue()); |
| |
| for (Node nextSibling = |
| currentNode |
| .getNextSibling(); (nextSibling != null) && ((nextSibling |
| .getNodeType() == Node.TEXT_NODE) || (nextSibling |
| .getNodeType() == Node |
| .CDATA_SECTION_NODE)); nextSibling = |
| nextSibling.getNextSibling()) { |
| |
| /* The XPath data model allows to select only the first of a |
| * sequence of mixed text and CDATA nodes. But we must output |
| * them all, so we must search: |
| * |
| * @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329 |
| */ |
| this.outputTextToWriter(nextSibling.getNodeValue()); |
| } |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludeSuffix); |
| } else { |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| break; |
| |
| case Node.ELEMENT_NODE : |
| Element currentElement = (Element) currentNode; |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludePrefix); |
| } else { |
| this._writer.write(HTMLExcludePrefix); |
| } |
| |
| this._writer.write("<"); |
| this._writer.write(currentElement.getTagName()); |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludeSuffix); |
| } else { |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| |
| // we output all Attrs which are available |
| NamedNodeMap attrs = currentElement.getAttributes(); |
| int attrsLength = attrs.getLength(); |
| Object attrs2[] = new Object[attrsLength]; |
| |
| for (int i = 0; i < attrsLength; i++) { |
| attrs2[i] = attrs.item(i); |
| } |
| |
| Object attrs3[] = |
| org.apache.xml.security.c14n.helper.C14nHelper |
| .sortAttributes(attrs2); |
| |
| for (int i = 0; i < attrsLength; i++) { |
| Attr a = (Attr) attrs3[i]; |
| boolean included = this._xpathNodeSet.contains(a); |
| boolean inclusive = |
| this._inclusiveNamespaces.contains(a.getName()); |
| |
| if (included) { |
| if (inclusive) { |
| |
| // included and inclusive |
| this._writer.write(HTMLIncludedInclusiveNamespacePrefix); |
| } else { |
| |
| // included and not inclusive |
| this._writer.write(HTMLIncludePrefix); |
| } |
| } else { |
| if (inclusive) { |
| |
| // excluded and inclusive |
| this._writer.write(HTMLExcludedInclusiveNamespacePrefix); |
| } else { |
| |
| // excluded and not inclusive |
| this._writer.write(HTMLExcludePrefix); |
| } |
| } |
| |
| this.outputAttrToWriter(a.getNodeName(), a.getNodeValue()); |
| |
| if (included) { |
| if (inclusive) { |
| |
| // included and inclusive |
| this._writer.write(HTMLIncludedInclusiveNamespaceSuffix); |
| } else { |
| |
| // included and not inclusive |
| this._writer.write(HTMLIncludeSuffix); |
| } |
| } else { |
| if (inclusive) { |
| |
| // excluded and inclusive |
| this._writer.write(HTMLExcludedInclusiveNamespaceSuffix); |
| } else { |
| |
| // excluded and not inclusive |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| } |
| } |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludePrefix); |
| } else { |
| this._writer.write(HTMLExcludePrefix); |
| } |
| |
| this._writer.write(">"); |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludeSuffix); |
| } else { |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| |
| // traversal |
| for (Node currentChild = currentNode.getFirstChild(); |
| currentChild != null; |
| currentChild = currentChild.getNextSibling()) { |
| this.canonicalizeXPathNodeSet(currentChild); |
| } |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludePrefix); |
| } else { |
| this._writer.write(HTMLExcludePrefix); |
| } |
| |
| this._writer.write("</"); |
| this._writer.write(currentElement.getTagName()); |
| this._writer.write(">"); |
| |
| if (this._xpathNodeSet.contains(currentNode)) { |
| this._writer.write(HTMLIncludeSuffix); |
| } else { |
| this._writer.write(HTMLExcludeSuffix); |
| } |
| break; |
| } |
| } |
| |
| |
| |
| /** |
| * Checks whether a Comment or ProcessingInstruction is before or after the |
| * document element. This is needed for prepending or appending "\n"s. |
| * |
| * @param currentNode comment or pi to check |
| * @return NODE_BEFORE_DOCUMENT_ELEMENT, NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or NODE_AFTER_DOCUMENT_ELEMENT |
| * @see NODE_BEFORE_DOCUMENT_ELEMENT |
| * @see NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT |
| * @see NODE_AFTER_DOCUMENT_ELEMENT |
| */ |
| private int getPositionRelativeToDocumentElement(Node currentNode) { |
| |
| if (currentNode == null) { |
| return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; |
| } |
| |
| Document doc = currentNode.getOwnerDocument(); |
| |
| if (currentNode.getParentNode() != doc) { |
| return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; |
| } |
| |
| Element documentElement = doc.getDocumentElement(); |
| |
| if (documentElement == null) { |
| return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; |
| } |
| |
| if (documentElement == currentNode) { |
| return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; |
| } |
| |
| for (Node x = currentNode; x != null; x = x.getNextSibling()) { |
| if (x == documentElement) { |
| return NODE_BEFORE_DOCUMENT_ELEMENT; |
| } |
| } |
| |
| return NODE_AFTER_DOCUMENT_ELEMENT; |
| } |
| |
| /** |
| * Normalizes an {@link Attr}ibute value |
| * |
| * The string value of the node is modified by replacing |
| * <UL> |
| * <LI>all ampersands (&) with <CODE>&amp;</CODE></LI> |
| * <LI>all open angle brackets (<) with <CODE>&lt;</CODE></LI> |
| * <LI>all quotation mark characters with <CODE>&quot;</CODE></LI> |
| * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, with character |
| * references. The character references are written in uppercase |
| * hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> is represented |
| * by the character reference <CODE>&#xD;</CODE>)</LI> |
| * </UL> |
| * |
| * @param name |
| * @param value |
| * @throws IOException |
| */ |
| private void outputAttrToWriter(String name, String value) |
| throws IOException { |
| |
| this._writer.write(" "); |
| this._writer.write(name); |
| this._writer.write("=\""); |
| |
| int length = value.length(); |
| |
| for (int i = 0; i < length; i++) { |
| char c = value.charAt(i); |
| |
| switch (c) { |
| |
| case '&' : |
| this._writer.write("&amp;"); |
| break; |
| |
| case '<' : |
| this._writer.write("&lt;"); |
| break; |
| |
| case '"' : |
| this._writer.write("&quot;"); |
| break; |
| |
| case 0x09 : // '\t' |
| this._writer.write("&#x9;"); |
| break; |
| |
| case 0x0A : // '\n' |
| this._writer.write("&#xA;"); |
| break; |
| |
| case 0x0D : // '\r' |
| this._writer.write("&#xD;"); |
| break; |
| |
| default : |
| this._writer.write(c); |
| break; |
| } |
| } |
| |
| this._writer.write("\""); |
| } |
| |
| /** |
| * Normalizes a {@link org.w3c.dom.Comment} value |
| * |
| * @param currentPI |
| * @throws IOException |
| */ |
| private void outputPItoWriter(ProcessingInstruction currentPI) |
| throws IOException { |
| |
| if (currentPI == null) { |
| return; |
| } |
| |
| this._writer.write("<?"); |
| |
| String target = currentPI.getTarget(); |
| int length = target.length(); |
| |
| for (int i = 0; i < length; i++) { |
| char c = target.charAt(i); |
| |
| switch (c) { |
| |
| case 0x0D : |
| this._writer.write("&#xD;"); |
| break; |
| |
| case ' ' : |
| this._writer.write("·"); |
| break; |
| |
| case '\n' : |
| this._writer.write("¶\n"); |
| break; |
| |
| default : |
| this._writer.write(c); |
| break; |
| } |
| } |
| |
| String data = currentPI.getData(); |
| |
| length = data.length(); |
| |
| if ((data != null) && (length > 0)) { |
| this._writer.write(" "); |
| |
| for (int i = 0; i < length; i++) { |
| char c = data.charAt(i); |
| |
| switch (c) { |
| |
| case 0x0D : |
| this._writer.write("&#xD;"); |
| break; |
| |
| default : |
| this._writer.write(c); |
| break; |
| } |
| } |
| } |
| |
| this._writer.write("?>"); |
| } |
| |
| /** |
| * Method outputCommentToWriter |
| * |
| * @param currentComment |
| * @throws IOException |
| */ |
| private void outputCommentToWriter(Comment currentComment) |
| throws IOException { |
| |
| if (currentComment == null) { |
| return; |
| } |
| |
| this._writer.write("<!--"); |
| |
| String data = currentComment.getData(); |
| int length = data.length(); |
| |
| for (int i = 0; i < length; i++) { |
| char c = data.charAt(i); |
| |
| switch (c) { |
| |
| case 0x0D : |
| this._writer.write("&#xD;"); |
| break; |
| |
| case ' ' : |
| this._writer.write("·"); |
| break; |
| |
| case '\n' : |
| this._writer.write("¶\n"); |
| break; |
| |
| default : |
| this._writer.write(c); |
| break; |
| } |
| } |
| |
| this._writer.write("-->"); |
| } |
| |
| /** |
| * Method outputTextToWriter |
| * |
| * @param text |
| * @throws IOException |
| */ |
| private void outputTextToWriter(String text) throws IOException { |
| |
| if (text == null) { |
| return; |
| } |
| |
| int length = text.length(); |
| |
| for (int i = 0; i < length; i++) { |
| char c = text.charAt(i); |
| |
| switch (c) { |
| |
| case '&' : |
| this._writer.write("&amp;"); |
| break; |
| |
| case '<' : |
| this._writer.write("&lt;"); |
| break; |
| |
| case '>' : |
| this._writer.write("&gt;"); |
| break; |
| |
| case 0xD : |
| this._writer.write("&#xD;"); |
| break; |
| |
| case ' ' : |
| this._writer.write("·"); |
| break; |
| |
| case '\n' : |
| this._writer.write("¶\n"); |
| break; |
| |
| default : |
| this._writer.write(c); |
| break; |
| } |
| } |
| } |
| } |
| } |