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  */
  23 package com.sun.org.apache.xml.internal.security.c14n;
  24 
  25 import java.io.ByteArrayInputStream;
  26 import java.io.InputStream;
  27 import java.io.OutputStream;
  28 import java.util.Map;
  29 import java.util.Set;
  30 import java.util.concurrent.ConcurrentHashMap;
  31 
  32 import javax.xml.XMLConstants;
  33 import javax.xml.parsers.DocumentBuilder;
  34 import javax.xml.parsers.DocumentBuilderFactory;
  35 
  36 import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer11_OmitComments;
  37 import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer11_WithComments;
  38 import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315ExclOmitComments;
  39 import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315ExclWithComments;
  40 import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315OmitComments;
  41 import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315WithComments;
  42 import com.sun.org.apache.xml.internal.security.c14n.implementations.CanonicalizerPhysical;
  43 import com.sun.org.apache.xml.internal.security.exceptions.AlgorithmAlreadyRegisteredException;
  44 import com.sun.org.apache.xml.internal.security.utils.JavaUtils;
  45 import org.w3c.dom.Document;
  46 import org.w3c.dom.Node;
  47 import org.w3c.dom.NodeList;
  48 import org.xml.sax.InputSource;
  49 
  50 /**
  51  *
  52  * @author Christian Geuer-Pollmann
  53  */
  54 public class Canonicalizer {
  55 
  56     /** The output encoding of canonicalized data */
  57     public static final String ENCODING = "UTF8";
  58 
  59     /**
  60      * XPath Expression for selecting every node and continuous comments joined
  61      * in only one node
  62      */
  63     public static final String XPATH_C14N_WITH_COMMENTS_SINGLE_NODE =
  64         "(.//. | .//@* | .//namespace::*)";
  65 
  66     /**
  67      * The URL defined in XML-SEC Rec for inclusive c14n <b>without</b> comments.
  68      */
  69     public static final String ALGO_ID_C14N_OMIT_COMMENTS =
  70         "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
  71     /**
  72      * The URL defined in XML-SEC Rec for inclusive c14n <b>with</b> comments.
  73      */
  74     public static final String ALGO_ID_C14N_WITH_COMMENTS =
  75         ALGO_ID_C14N_OMIT_COMMENTS + "#WithComments";
  76     /**
  77      * The URL defined in XML-SEC Rec for exclusive c14n <b>without</b> comments.
  78      */
  79     public static final String ALGO_ID_C14N_EXCL_OMIT_COMMENTS =
  80         "http://www.w3.org/2001/10/xml-exc-c14n#";
  81     /**
  82      * The URL defined in XML-SEC Rec for exclusive c14n <b>with</b> comments.
  83      */
  84     public static final String ALGO_ID_C14N_EXCL_WITH_COMMENTS =
  85         ALGO_ID_C14N_EXCL_OMIT_COMMENTS + "WithComments";
  86     /**
  87      * The URI for inclusive c14n 1.1 <b>without</b> comments.
  88      */
  89     public static final String ALGO_ID_C14N11_OMIT_COMMENTS =
  90         "http://www.w3.org/2006/12/xml-c14n11";
  91     /**
  92      * The URI for inclusive c14n 1.1 <b>with</b> comments.
  93      */
  94     public static final String ALGO_ID_C14N11_WITH_COMMENTS =
  95         ALGO_ID_C14N11_OMIT_COMMENTS + "#WithComments";
  96     /**
  97      * Non-standard algorithm to serialize the physical representation for XML Encryption
  98      */
  99     public static final String ALGO_ID_C14N_PHYSICAL =
 100         "http://santuario.apache.org/c14n/physical";
 101 
 102     private static Map<String, Class<? extends CanonicalizerSpi>> canonicalizerHash =
 103         new ConcurrentHashMap<String, Class<? extends CanonicalizerSpi>>();
 104 
 105     private final CanonicalizerSpi canonicalizerSpi;
 106 
 107     /**
 108      * Constructor Canonicalizer
 109      *
 110      * @param algorithmURI
 111      * @throws InvalidCanonicalizerException
 112      */
 113     private Canonicalizer(String algorithmURI) throws InvalidCanonicalizerException {
 114         try {
 115             Class<? extends CanonicalizerSpi> implementingClass =
 116                 canonicalizerHash.get(algorithmURI);
 117 
 118             @SuppressWarnings("deprecation")
 119             CanonicalizerSpi tmp = implementingClass.newInstance();
 120             canonicalizerSpi = tmp;
 121             canonicalizerSpi.reset = true;
 122         } catch (Exception e) {
 123             Object exArgs[] = { algorithmURI };
 124             throw new InvalidCanonicalizerException(
 125                 "signature.Canonicalizer.UnknownCanonicalizer", exArgs, e
 126             );
 127         }
 128     }
 129 
 130     /**
 131      * Method getInstance
 132      *
 133      * @param algorithmURI
 134      * @return a Canonicalizer instance ready for the job
 135      * @throws InvalidCanonicalizerException
 136      */
 137     public static final Canonicalizer getInstance(String algorithmURI)
 138         throws InvalidCanonicalizerException {
 139         return new Canonicalizer(algorithmURI);
 140     }
 141 
 142     /**
 143      * Method register
 144      *
 145      * @param algorithmURI
 146      * @param implementingClass
 147      * @throws AlgorithmAlreadyRegisteredException
 148      * @throws SecurityException if a security manager is installed and the
 149      *    caller does not have permission to register the canonicalizer
 150      */
 151     @SuppressWarnings("unchecked")
 152     public static void register(String algorithmURI, String implementingClass)
 153         throws AlgorithmAlreadyRegisteredException, ClassNotFoundException {
 154         JavaUtils.checkRegisterPermission();
 155         // check whether URI is already registered
 156         Class<? extends CanonicalizerSpi> registeredClass =
 157             canonicalizerHash.get(algorithmURI);
 158 
 159         if (registeredClass != null)  {
 160             Object exArgs[] = { algorithmURI, registeredClass };
 161             throw new AlgorithmAlreadyRegisteredException("algorithm.alreadyRegistered", exArgs);
 162         }
 163 
 164         canonicalizerHash.put(
 165             algorithmURI, (Class<? extends CanonicalizerSpi>)Class.forName(implementingClass)
 166         );
 167     }
 168 
 169     /**
 170      * Method register
 171      *
 172      * @param algorithmURI
 173      * @param implementingClass
 174      * @throws AlgorithmAlreadyRegisteredException
 175      * @throws SecurityException if a security manager is installed and the
 176      *    caller does not have permission to register the canonicalizer
 177      */
 178     public static void register(String algorithmURI, Class<? extends CanonicalizerSpi> implementingClass)
 179         throws AlgorithmAlreadyRegisteredException, ClassNotFoundException {
 180         JavaUtils.checkRegisterPermission();
 181         // check whether URI is already registered
 182         Class<? extends CanonicalizerSpi> registeredClass = canonicalizerHash.get(algorithmURI);
 183 
 184         if (registeredClass != null)  {
 185             Object exArgs[] = { algorithmURI, registeredClass };
 186             throw new AlgorithmAlreadyRegisteredException("algorithm.alreadyRegistered", exArgs);
 187         }
 188 
 189         canonicalizerHash.put(algorithmURI, implementingClass);
 190     }
 191 
 192     /**
 193      * This method registers the default algorithms.
 194      */
 195     public static void registerDefaultAlgorithms() {
 196         canonicalizerHash.put(
 197             Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS,
 198             Canonicalizer20010315OmitComments.class
 199         );
 200         canonicalizerHash.put(
 201             Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS,
 202             Canonicalizer20010315WithComments.class
 203         );
 204         canonicalizerHash.put(
 205             Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS,
 206             Canonicalizer20010315ExclOmitComments.class
 207         );
 208         canonicalizerHash.put(
 209             Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS,
 210             Canonicalizer20010315ExclWithComments.class
 211         );
 212         canonicalizerHash.put(
 213             Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS,
 214             Canonicalizer11_OmitComments.class
 215         );
 216         canonicalizerHash.put(
 217             Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS,
 218             Canonicalizer11_WithComments.class
 219         );
 220         canonicalizerHash.put(
 221             Canonicalizer.ALGO_ID_C14N_PHYSICAL,
 222             CanonicalizerPhysical.class
 223         );
 224     }
 225 
 226     /**
 227      * Method getURI
 228      *
 229      * @return the URI defined for this c14n instance.
 230      */
 231     public final String getURI() {
 232         return canonicalizerSpi.engineGetURI();
 233     }
 234 
 235     /**
 236      * Method getIncludeComments
 237      *
 238      * @return true if the c14n respect the comments.
 239      */
 240     public boolean getIncludeComments() {
 241         return canonicalizerSpi.engineGetIncludeComments();
 242     }
 243 
 244     /**
 245      * This method tries to canonicalize the given bytes. It's possible to even
 246      * canonicalize non-wellformed sequences if they are well-formed after being
 247      * wrapped with a <CODE>&gt;a&lt;...&gt;/a&lt;</CODE>.
 248      *
 249      * @param inputBytes
 250      * @return the result of the canonicalization.
 251      * @throws CanonicalizationException
 252      * @throws java.io.IOException
 253      * @throws javax.xml.parsers.ParserConfigurationException
 254      * @throws org.xml.sax.SAXException
 255      */
 256     public byte[] canonicalize(byte[] inputBytes)
 257         throws javax.xml.parsers.ParserConfigurationException,
 258         java.io.IOException, org.xml.sax.SAXException, CanonicalizationException {
 259         InputStream bais = new ByteArrayInputStream(inputBytes);
 260         InputSource in = new InputSource(bais);
 261         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
 262         dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
 263 
 264         dfactory.setNamespaceAware(true);
 265 
 266         // needs to validate for ID attribute normalization
 267         dfactory.setValidating(true);
 268 
 269         DocumentBuilder db = dfactory.newDocumentBuilder();
 270 
 271         /*
 272          * for some of the test vectors from the specification,
 273          * there has to be a validating parser for ID attributes, default
 274          * attribute values, NMTOKENS, etc.
 275          * Unfortunately, the test vectors do use different DTDs or
 276          * even no DTD. So Xerces 1.3.1 fires many warnings about using
 277          * ErrorHandlers.
 278          *
 279          * Text from the spec:
 280          *
 281          * The input octet stream MUST contain a well-formed XML document,
 282          * but the input need not be validated. However, the attribute
 283          * value normalization and entity reference resolution MUST be
 284          * performed in accordance with the behaviors of a validating
 285          * XML processor. As well, nodes for default attributes (declared
 286          * in the ATTLIST with an AttValue but not specified) are created
 287          * in each element. Thus, the declarations in the document type
 288          * declaration are used to help create the canonical form, even
 289          * though the document type declaration is not retained in the
 290          * canonical form.
 291          */
 292         db.setErrorHandler(new com.sun.org.apache.xml.internal.security.utils.IgnoreAllErrorHandler());
 293 
 294         Document document = db.parse(in);
 295         return this.canonicalizeSubtree(document);
 296     }
 297 
 298     /**
 299      * Canonicalizes the subtree rooted by <CODE>node</CODE>.
 300      *
 301      * @param node The node to canonicalize
 302      * @return the result of the c14n.
 303      *
 304      * @throws CanonicalizationException
 305      */
 306     public byte[] canonicalizeSubtree(Node node) throws CanonicalizationException {
 307         return canonicalizerSpi.engineCanonicalizeSubTree(node);
 308     }
 309 
 310     /**
 311      * Canonicalizes the subtree rooted by <CODE>node</CODE>.
 312      *
 313      * @param node
 314      * @param inclusiveNamespaces
 315      * @return the result of the c14n.
 316      * @throws CanonicalizationException
 317      */
 318     public byte[] canonicalizeSubtree(Node node, String inclusiveNamespaces)
 319         throws CanonicalizationException {
 320         return canonicalizerSpi.engineCanonicalizeSubTree(node, inclusiveNamespaces);
 321     }
 322 
 323     /**
 324      * Canonicalizes an XPath node set. The <CODE>xpathNodeSet</CODE> is treated
 325      * as a list of XPath nodes, not as a list of subtrees.
 326      *
 327      * @param xpathNodeSet
 328      * @return the result of the c14n.
 329      * @throws CanonicalizationException
 330      */
 331     public byte[] canonicalizeXPathNodeSet(NodeList xpathNodeSet)
 332         throws CanonicalizationException {
 333         return canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet);
 334     }
 335 
 336     /**
 337      * Canonicalizes an XPath node set. The <CODE>xpathNodeSet</CODE> is treated
 338      * as a list of XPath nodes, not as a list of subtrees.
 339      *
 340      * @param xpathNodeSet
 341      * @param inclusiveNamespaces
 342      * @return the result of the c14n.
 343      * @throws CanonicalizationException
 344      */
 345     public byte[] canonicalizeXPathNodeSet(
 346         NodeList xpathNodeSet, String inclusiveNamespaces
 347     ) throws CanonicalizationException {
 348         return
 349             canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, inclusiveNamespaces);
 350     }
 351 
 352     /**
 353      * Canonicalizes an XPath node set.
 354      *
 355      * @param xpathNodeSet
 356      * @return the result of the c14n.
 357      * @throws CanonicalizationException
 358      */
 359     public byte[] canonicalizeXPathNodeSet(Set<Node> xpathNodeSet)
 360         throws CanonicalizationException {
 361         return canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet);
 362     }
 363 
 364     /**
 365      * Canonicalizes an XPath node set.
 366      *
 367      * @param xpathNodeSet
 368      * @param inclusiveNamespaces
 369      * @return the result of the c14n.
 370      * @throws CanonicalizationException
 371      */
 372     public byte[] canonicalizeXPathNodeSet(
 373         Set<Node> xpathNodeSet, String inclusiveNamespaces
 374     ) throws CanonicalizationException {
 375         return
 376             canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, inclusiveNamespaces);
 377     }
 378 
 379     /**
 380      * Sets the writer where the canonicalization ends.  ByteArrayOutputStream
 381      * if none is set.
 382      * @param os
 383      */
 384     public void setWriter(OutputStream os) {
 385         canonicalizerSpi.setWriter(os);
 386     }
 387 
 388     /**
 389      * Returns the name of the implementing {@link CanonicalizerSpi} class
 390      *
 391      * @return the name of the implementing {@link CanonicalizerSpi} class
 392      */
 393     public String getImplementingCanonicalizerClass() {
 394         return canonicalizerSpi.getClass().getName();
 395     }
 396 
 397     /**
 398      * Set the canonicalizer behaviour to not reset.
 399      */
 400     public void notReset() {
 401         canonicalizerSpi.reset = false;
 402     }
 403 
 404 }