blob: 32aaac393991e8329419a5fd699a3d351aa410b8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.xml.security.c14n.implementations;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.helper.C14nHelper;
import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.transforms.params.InclusiveNamespaces;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
/**
* Implements &quot; <A
* HREF="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/">Exclusive XML
* Canonicalization, Version 1.0 </A>&quot; <p></p>
* Credits: During restructuring of the Canonicalizer framework, Ren??
* Kollmorgen from Software AG submitted an implementation of ExclC14n which
* fitted into the old architecture and which based heavily on my old (and slow)
* implementation of "Canonical XML". A big "thank you" to Ren?? for this.
* <p></p>
* <i>THIS </i> implementation is a complete rewrite of the algorithm.
*
* @see <a href="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/">
* Exclusive XML Canonicalization, Version 1.0</a>
*/
public abstract class Canonicalizer20010315Excl extends CanonicalizerBase {
/**
* This Set contains the names (Strings like "xmlns" or "xmlns:foo") of
* the inclusive namespaces.
*/
private SortedSet<String> inclusiveNSSet;
private boolean propagateDefaultNamespace = false;
/**
* Constructor Canonicalizer20010315Excl
*
* @param includeComments
*/
public Canonicalizer20010315Excl(boolean includeComments) {
super(includeComments);
}
/**
* Method engineCanonicalizeSubTree
* {@inheritDoc}
* @param rootNode
*
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeSubTree(Node rootNode)
throws CanonicalizationException {
return engineCanonicalizeSubTree(rootNode, "", null);
}
/**
* Method engineCanonicalizeSubTree
* {@inheritDoc}
* @param rootNode
* @param inclusiveNamespaces
*
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeSubTree(
Node rootNode, String inclusiveNamespaces
) throws CanonicalizationException {
return engineCanonicalizeSubTree(rootNode, inclusiveNamespaces, null);
}
/**
* Method engineCanonicalizeSubTree
* {@inheritDoc}
* @param rootNode
* @param inclusiveNamespaces
* @param propagateDefaultNamespace If true the default namespace will be propagated to the c14n-ized root element
*
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeSubTree(
Node rootNode, String inclusiveNamespaces, boolean propagateDefaultNamespace
) throws CanonicalizationException {
this.propagateDefaultNamespace = propagateDefaultNamespace;
return engineCanonicalizeSubTree(rootNode, inclusiveNamespaces, null);
}
/**
* Method engineCanonicalizeSubTree
* @param rootNode
* @param inclusiveNamespaces
* @param excl A element to exclude from the c14n process.
* @return the rootNode c14n.
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeSubTree(
Node rootNode, String inclusiveNamespaces, Node excl
) throws CanonicalizationException{
inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
return super.engineCanonicalizeSubTree(rootNode, excl);
}
/**
*
* @param rootNode
* @param inclusiveNamespaces
* @return the rootNode c14n.
* @throws CanonicalizationException
*/
public byte[] engineCanonicalize(
XMLSignatureInput rootNode, String inclusiveNamespaces
) throws CanonicalizationException {
inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
return super.engineCanonicalize(rootNode);
}
/**
* Method engineCanonicalizeXPathNodeSet
* {@inheritDoc}
* @param xpathNodeSet
* @param inclusiveNamespaces
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeXPathNodeSet(
Set<Node> xpathNodeSet, String inclusiveNamespaces
) throws CanonicalizationException {
inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
return super.engineCanonicalizeXPathNodeSet(xpathNodeSet);
}
@Override
protected void outputAttributesSubtree(Element element, NameSpaceSymbTable ns,
Map<String, byte[]> cache)
throws CanonicalizationException, DOMException, IOException {
// result will contain the attrs which have to be output
SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
// The prefix visibly utilized (in the attribute or in the name) in
// the element
SortedSet<String> visiblyUtilized = new TreeSet<String>();
if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) {
visiblyUtilized.addAll(inclusiveNSSet);
}
if (element.hasAttributes()) {
NamedNodeMap attrs = element.getAttributes();
int attrsLength = attrs.getLength();
for (int i = 0; i < attrsLength; i++) {
Attr attribute = (Attr) attrs.item(i);
String NName = attribute.getLocalName();
String NNodeValue = attribute.getNodeValue();
if (!XMLNS_URI.equals(attribute.getNamespaceURI())) {
// Not a namespace definition.
// The Element is output element, add the prefix (if used) to
// visiblyUtilized
String prefix = attribute.getPrefix();
if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) {
visiblyUtilized.add(prefix);
}
// Add to the result.
result.add(attribute);
} else if (!(XML.equals(NName) && XML_LANG_URI.equals(NNodeValue))
&& ns.addMapping(NName, NNodeValue, attribute)
&& C14nHelper.namespaceIsRelative(NNodeValue)) {
// The default mapping for xml must not be output.
// New definition check if it is relative.
Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()};
throw new CanonicalizationException(
"c14n.Canonicalizer.RelativeNamespace", exArgs
);
}
}
}
if (propagateDefaultNamespace && ns.getLevel() == 1 &&
inclusiveNSSet.contains(XMLNS) &&
ns.getMappingWithoutRendered(XMLNS) == null) {
ns.removeMapping(XMLNS);
ns.addMapping(
XMLNS, "", getNullNode(element.getOwnerDocument()));
}
String prefix = null;
if (element.getNamespaceURI() != null
&& !(element.getPrefix() == null || element.getPrefix().length() == 0)) {
prefix = element.getPrefix();
} else {
prefix = XMLNS;
}
visiblyUtilized.add(prefix);
for (String s : visiblyUtilized) {
Attr key = ns.getMapping(s);
if (key != null) {
result.add(key);
}
}
OutputStream writer = getWriter();
//we output all Attrs which are available
for (Attr attr : result) {
outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void outputAttributes(Element element, NameSpaceSymbTable ns,
Map<String, byte[]> cache)
throws CanonicalizationException, DOMException, IOException {
// result will contain the attrs which have to be output
SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
// The prefix visibly utilized (in the attribute or in the name) in
// the element
Set<String> visiblyUtilized = null;
// It's the output selected.
boolean isOutputElement = isVisibleDO(element, ns.getLevel()) == 1;
if (isOutputElement) {
visiblyUtilized = new TreeSet<String>();
if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) {
visiblyUtilized.addAll(inclusiveNSSet);
}
}
if (element.hasAttributes()) {
NamedNodeMap attrs = element.getAttributes();
int attrsLength = attrs.getLength();
for (int i = 0; i < attrsLength; i++) {
Attr attribute = (Attr) attrs.item(i);
String NName = attribute.getLocalName();
String NNodeValue = attribute.getNodeValue();
if (!XMLNS_URI.equals(attribute.getNamespaceURI())) {
if (isVisible(attribute) && isOutputElement) {
// The Element is output element, add the prefix (if used)
// to visibyUtilized
String prefix = attribute.getPrefix();
if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) {
visiblyUtilized.add(prefix);
}
// Add to the result.
result.add(attribute);
}
} else if (isOutputElement && !isVisible(attribute) && !XMLNS.equals(NName)) {
ns.removeMappingIfNotRender(NName);
} else {
if (!isOutputElement && isVisible(attribute)
&& inclusiveNSSet.contains(NName)
&& !ns.removeMappingIfRender(NName)) {
Node n = ns.addMappingAndRender(NName, NNodeValue, attribute);
if (n != null) {
result.add((Attr)n);
if (C14nHelper.namespaceIsRelative(attribute)) {
Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
throw new CanonicalizationException(
"c14n.Canonicalizer.RelativeNamespace", exArgs
);
}
}
}
if (ns.addMapping(NName, NNodeValue, attribute)
&& C14nHelper.namespaceIsRelative(NNodeValue)) {
// New definition check if it is relative
Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
throw new CanonicalizationException(
"c14n.Canonicalizer.RelativeNamespace", exArgs
);
}
}
}
}
if (isOutputElement) {
// The element is visible, handle the xmlns definition
Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
if (xmlns != null && !isVisible(xmlns)) {
// There is a definition but the xmlns is not selected by the
// xpath. then xmlns=""
ns.addMapping(XMLNS, "", getNullNode(xmlns.getOwnerDocument()));
}
String prefix = null;
if (element.getNamespaceURI() != null
&& !(element.getPrefix() == null || element.getPrefix().length() == 0)) {
prefix = element.getPrefix();
} else {
prefix = XMLNS;
}
visiblyUtilized.add(prefix);
for (String s : visiblyUtilized) {
Attr key = ns.getMapping(s);
if (key != null) {
result.add(key);
}
}
}
OutputStream writer = getWriter();
//we output all Attrs which are available
for (Attr attr : result) {
outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
}
}
protected void circumventBugIfNeeded(XMLSignatureInput input)
throws CanonicalizationException, ParserConfigurationException,
IOException, SAXException {
if (!input.isNeedsToBeExpanded() || inclusiveNSSet.isEmpty()) {
return;
}
Document doc = null;
if (input.getSubNode() != null) {
doc = XMLUtils.getOwnerDocument(input.getSubNode());
} else {
doc = XMLUtils.getOwnerDocument(input.getNodeSet());
}
XMLUtils.circumventBug2650(doc);
}
}