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.IOException; 22 import java.util.Iterator; 23 import java.util.Set; 24 import java.util.SortedSet; 25 import java.util.TreeSet; 26 import javax.xml.parsers.ParserConfigurationException; 27 28 import org.apache.xml.security.c14n.CanonicalizationException; 29 import org.apache.xml.security.c14n.helper.C14nHelper; 30 import org.apache.xml.security.signature.XMLSignatureInput; 31 import org.apache.xml.security.transforms.params.InclusiveNamespaces; 32 import org.apache.xml.security.utils.Constants; 33 import org.apache.xml.security.utils.XMLUtils; 34 import org.w3c.dom.Attr; 35 import org.w3c.dom.Document; 36 import org.w3c.dom.Element; 37 import org.w3c.dom.NamedNodeMap; 38 import org.w3c.dom.Node; 39 import org.xml.sax.SAXException; 40 41 /** 42 * Implements " <A 43 * HREF="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/">Exclusive XML 44 * Canonicalization, Version 1.0 </A>" <BR /> 45 * Credits: During restructuring of the Canonicalizer framework, Ren?? 46 * Kollmorgen from Software AG submitted an implementation of ExclC14n which 47 * fitted into the old architecture and which based heavily on my old (and slow) 48 * implementation of "Canonical XML". A big "thank you" to Ren?? for this. 49 * <BR /> 50 * <i>THIS </i> implementation is a complete rewrite of the algorithm. 51 * 52 * @author Christian Geuer-Pollmann <geuerp@apache.org> 53 * @version $Revision: 1147448 $ 54 * @see <a href="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/ Exclusive#"> 55 * XML Canonicalization, Version 1.0</a> 56 */ 57 public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { 58 59 private static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS; 60 private static final String XMLNS_URI = Constants.NamespaceSpecNS; 61 62 /** 63 * This Set contains the names (Strings like "xmlns" or "xmlns:foo") of 64 * the inclusive namespaces. 65 */ 66 private SortedSet<String> inclusiveNSSet; 67 68 private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE); 69 70 /** 71 * Constructor Canonicalizer20010315Excl 72 * 73 * @param includeComments 74 */ Canonicalizer20010315Excl(boolean includeComments)75 public Canonicalizer20010315Excl(boolean includeComments) { 76 super(includeComments); 77 } 78 79 /** 80 * Method engineCanonicalizeSubTree 81 * @inheritDoc 82 * @param rootNode 83 * 84 * @throws CanonicalizationException 85 */ engineCanonicalizeSubTree(Node rootNode)86 public byte[] engineCanonicalizeSubTree(Node rootNode) 87 throws CanonicalizationException { 88 return engineCanonicalizeSubTree(rootNode, "", null); 89 } 90 91 /** 92 * Method engineCanonicalizeSubTree 93 * @inheritDoc 94 * @param rootNode 95 * @param inclusiveNamespaces 96 * 97 * @throws CanonicalizationException 98 */ engineCanonicalizeSubTree( Node rootNode, String inclusiveNamespaces )99 public byte[] engineCanonicalizeSubTree( 100 Node rootNode, String inclusiveNamespaces 101 ) throws CanonicalizationException { 102 return engineCanonicalizeSubTree(rootNode, inclusiveNamespaces, null); 103 } 104 105 /** 106 * Method engineCanonicalizeSubTree 107 * @param rootNode 108 * @param inclusiveNamespaces 109 * @param excl A element to exclude from the c14n process. 110 * @return the rootNode c14n. 111 * @throws CanonicalizationException 112 */ engineCanonicalizeSubTree( Node rootNode, String inclusiveNamespaces, Node excl )113 public byte[] engineCanonicalizeSubTree( 114 Node rootNode, String inclusiveNamespaces, Node excl 115 ) throws CanonicalizationException{ 116 inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces); 117 return super.engineCanonicalizeSubTree(rootNode, excl); 118 } 119 120 /** 121 * 122 * @param rootNode 123 * @param inclusiveNamespaces 124 * @return the rootNode c14n. 125 * @throws CanonicalizationException 126 */ engineCanonicalize( XMLSignatureInput rootNode, String inclusiveNamespaces )127 public byte[] engineCanonicalize( 128 XMLSignatureInput rootNode, String inclusiveNamespaces 129 ) throws CanonicalizationException { 130 inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces); 131 return super.engineCanonicalize(rootNode); 132 } 133 134 /** 135 * Method engineCanonicalizeXPathNodeSet 136 * @inheritDoc 137 * @param xpathNodeSet 138 * @param inclusiveNamespaces 139 * @throws CanonicalizationException 140 */ engineCanonicalizeXPathNodeSet( Set<Node> xpathNodeSet, String inclusiveNamespaces )141 public byte[] engineCanonicalizeXPathNodeSet( 142 Set<Node> xpathNodeSet, String inclusiveNamespaces 143 ) throws CanonicalizationException { 144 inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces); 145 return super.engineCanonicalizeXPathNodeSet(xpathNodeSet); 146 } 147 148 @Override handleAttributesSubtree(Element element, NameSpaceSymbTable ns)149 protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns) 150 throws CanonicalizationException { 151 // result will contain the attrs which have to be output 152 final SortedSet<Attr> result = this.result; 153 result.clear(); 154 155 // The prefix visibly utilized (in the attribute or in the name) in 156 // the element 157 SortedSet<String> visiblyUtilized = new TreeSet<String>(); 158 if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) { 159 visiblyUtilized.addAll(inclusiveNSSet); 160 } 161 162 if (element.hasAttributes()) { 163 NamedNodeMap attrs = element.getAttributes(); 164 int attrsLength = attrs.getLength(); 165 for (int i = 0; i < attrsLength; i++) { 166 Attr attribute = (Attr) attrs.item(i); 167 String NName = attribute.getLocalName(); 168 String NNodeValue = attribute.getNodeValue(); 169 170 if (!XMLNS_URI.equals(attribute.getNamespaceURI())) { 171 // Not a namespace definition. 172 // The Element is output element, add the prefix (if used) to 173 // visiblyUtilized 174 String prefix = attribute.getPrefix(); 175 if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) { 176 visiblyUtilized.add(prefix); 177 } 178 // Add to the result. 179 result.add(attribute); 180 } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NNodeValue)) 181 && ns.addMapping(NName, NNodeValue, attribute) 182 && C14nHelper.namespaceIsRelative(NNodeValue)) { 183 // The default mapping for xml must not be output. 184 // New definition check if it is relative. 185 Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()}; 186 throw new CanonicalizationException( 187 "c14n.Canonicalizer.RelativeNamespace", exArgs 188 ); 189 } 190 } 191 } 192 String prefix = null; 193 if (element.getNamespaceURI() != null 194 && !(element.getPrefix() == null || element.getPrefix().length() == 0)) { 195 prefix = element.getPrefix(); 196 } else { 197 prefix = XMLNS; 198 } 199 visiblyUtilized.add(prefix); 200 201 for (String s : visiblyUtilized) { 202 Attr key = ns.getMapping(s); 203 if (key != null) { 204 result.add(key); 205 } 206 } 207 208 return result.iterator(); 209 } 210 211 /** 212 * @inheritDoc 213 * @param element 214 * @throws CanonicalizationException 215 */ 216 @Override handleAttributes(Element element, NameSpaceSymbTable ns)217 protected final Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns) 218 throws CanonicalizationException { 219 // result will contain the attrs which have to be output 220 final SortedSet<Attr> result = this.result; 221 result.clear(); 222 223 // The prefix visibly utilized (in the attribute or in the name) in 224 // the element 225 Set<String> visiblyUtilized = null; 226 // It's the output selected. 227 boolean isOutputElement = isVisibleDO(element, ns.getLevel()) == 1; 228 if (isOutputElement) { 229 visiblyUtilized = new TreeSet<String>(); 230 if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) { 231 visiblyUtilized.addAll(inclusiveNSSet); 232 } 233 } 234 235 if (element.hasAttributes()) { 236 NamedNodeMap attrs = element.getAttributes(); 237 int attrsLength = attrs.getLength(); 238 for (int i = 0; i < attrsLength; i++) { 239 Attr attribute = (Attr) attrs.item(i); 240 241 String NName = attribute.getLocalName(); 242 String NNodeValue = attribute.getNodeValue(); 243 244 if (!XMLNS_URI.equals(attribute.getNamespaceURI())) { 245 if (isVisible(attribute) && isOutputElement) { 246 // The Element is output element, add the prefix (if used) 247 // to visibyUtilized 248 String prefix = attribute.getPrefix(); 249 if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) { 250 visiblyUtilized.add(prefix); 251 } 252 // Add to the result. 253 result.add(attribute); 254 } 255 } else if (isOutputElement && !isVisible(attribute) && !XMLNS.equals(NName)) { 256 ns.removeMappingIfNotRender(NName); 257 } else { 258 if (!isOutputElement && isVisible(attribute) 259 && inclusiveNSSet.contains(NName) 260 && !ns.removeMappingIfRender(NName)) { 261 Node n = ns.addMappingAndRender(NName, NNodeValue, attribute); 262 if (n != null) { 263 result.add((Attr)n); 264 if (C14nHelper.namespaceIsRelative(attribute)) { 265 Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() }; 266 throw new CanonicalizationException( 267 "c14n.Canonicalizer.RelativeNamespace", exArgs 268 ); 269 } 270 } 271 } 272 273 if (ns.addMapping(NName, NNodeValue, attribute) 274 && C14nHelper.namespaceIsRelative(NNodeValue)) { 275 // New definition check if it is relative 276 Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() }; 277 throw new CanonicalizationException( 278 "c14n.Canonicalizer.RelativeNamespace", exArgs 279 ); 280 } 281 } 282 } 283 } 284 285 if (isOutputElement) { 286 // The element is visible, handle the xmlns definition 287 Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS); 288 if (xmlns != null && !isVisible(xmlns)) { 289 // There is a definition but the xmlns is not selected by the 290 // xpath. then xmlns="" 291 ns.addMapping(XMLNS, "", nullNode); 292 } 293 294 String prefix = null; 295 if (element.getNamespaceURI() != null 296 && !(element.getPrefix() == null || element.getPrefix().length() == 0)) { 297 prefix = element.getPrefix(); 298 } else { 299 prefix = XMLNS; 300 } 301 visiblyUtilized.add(prefix); 302 303 for (String s : visiblyUtilized) { 304 Attr key = ns.getMapping(s); 305 if (key != null) { 306 result.add(key); 307 } 308 } 309 } 310 311 return result.iterator(); 312 } 313 circumventBugIfNeeded(XMLSignatureInput input)314 protected void circumventBugIfNeeded(XMLSignatureInput input) 315 throws CanonicalizationException, ParserConfigurationException, 316 IOException, SAXException { 317 if (!input.isNeedsToBeExpanded() || inclusiveNSSet.isEmpty() || inclusiveNSSet.isEmpty()) { 318 return; 319 } 320 Document doc = null; 321 if (input.getSubNode() != null) { 322 doc = XMLUtils.getOwnerDocument(input.getSubNode()); 323 } else { 324 doc = XMLUtils.getOwnerDocument(input.getNodeSet()); 325 } 326 XMLUtils.circumventBug2650(doc); 327 } 328 } 329