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             canonicalizerSpi = implementingClass.newInstance();
 119             canonicalizerSpi.reset = true;
 120         } catch (Exception e) {
 121             Object exArgs[] = { algorithmURI };
 122             throw new InvalidCanonicalizerException(
 123                 "signature.Canonicalizer.UnknownCanonicalizer", exArgs, e
 124             );
 125         }
 126     }
 127 
 128     /**
 129      * Method getInstance
 130      *
 131      * @param algorithmURI
 132      * @return a Canonicalizer instance ready for the job
 133      * @throws InvalidCanonicalizerException
 134      */
 135     public static final Canonicalizer getInstance(String algorithmURI)
 136         throws InvalidCanonicalizerException {
 137         return new Canonicalizer(algorithmURI);
 138     }
 139 
 140     /**
 141      * Method register
 142      *
 143      * @param algorithmURI
 144      * @param implementingClass
 145      * @throws AlgorithmAlreadyRegisteredException
 146      * @throws SecurityException if a security manager is installed and the
 147      *    caller does not have permission to register the canonicalizer
 148      */
 149     @SuppressWarnings("unchecked")
 150     public static void register(String algorithmURI, String implementingClass)
 151         throws AlgorithmAlreadyRegisteredException, ClassNotFoundException {
 152         JavaUtils.checkRegisterPermission();
 153         // check whether URI is already registered
 154         Class<? extends CanonicalizerSpi> registeredClass =
 155             canonicalizerHash.get(algorithmURI);
 156 
 157         if (registeredClass != null)  {
 158             Object exArgs[] = { algorithmURI, registeredClass };
 159             throw new AlgorithmAlreadyRegisteredException("algorithm.alreadyRegistered", exArgs);
 160         }
 161 
 162         canonicalizerHash.put(
 163             algorithmURI, (Class<? extends CanonicalizerSpi>)Class.forName(implementingClass)
 164         );
 165     }
 166 
 167     /**
 168      * Method register
 169      *
 170      * @param algorithmURI
 171      * @param implementingClass
 172      * @throws AlgorithmAlreadyRegisteredException
 173      * @throws SecurityException if a security manager is installed and the
 174      *    caller does not have permission to register the canonicalizer
 175      */
 176     public static void register(String algorithmURI, Class<? extends CanonicalizerSpi> implementingClass)
 177         throws AlgorithmAlreadyRegisteredException, ClassNotFoundException {
 178         JavaUtils.checkRegisterPermission();
 179         // check whether URI is already registered
 180         Class<? extends CanonicalizerSpi> registeredClass = canonicalizerHash.get(algorithmURI);
 181 
 182         if (registeredClass != null)  {
 183             Object exArgs[] = { algorithmURI, registeredClass };
 184             throw new AlgorithmAlreadyRegisteredException("algorithm.alreadyRegistered", exArgs);
 185         }
 186 
 187         canonicalizerHash.put(algorithmURI, implementingClass);
 188     }
 189 
 190     /**
 191      * This method registers the default algorithms.
 192      */
 193     public static void registerDefaultAlgorithms() {
 194         canonicalizerHash.put(
 195             Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS,
 196             Canonicalizer20010315OmitComments.class
 197         );
 198         canonicalizerHash.put(
 199             Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS,
 200             Canonicalizer20010315WithComments.class
 201         );
 202         canonicalizerHash.put(
 203             Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS,
 204             Canonicalizer20010315ExclOmitComments.class
 205         );
 206         canonicalizerHash.put(
 207             Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS,
 208             Canonicalizer20010315ExclWithComments.class
 209         );
 210         canonicalizerHash.put(
 211             Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS,
 212             Canonicalizer11_OmitComments.class
 213         );
 214         canonicalizerHash.put(
 215             Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS,
 216             Canonicalizer11_WithComments.class
 217         );
 218         canonicalizerHash.put(
 219             Canonicalizer.ALGO_ID_C14N_PHYSICAL,
 220             CanonicalizerPhysical.class
 221         );
 222     }
 223 
 224     /**
 225      * Method getURI
 226      *
 227      * @return the URI defined for this c14n instance.
 228      */
 229     public final String getURI() {
 230         return canonicalizerSpi.engineGetURI();
 231     }
 232 
 233     /**
 234      * Method getIncludeComments
 235      *
 236      * @return true if the c14n respect the comments.
 237      */
 238     public boolean getIncludeComments() {
 239         return canonicalizerSpi.engineGetIncludeComments();
 240     }
 241 
 242     /**
 243      * This method tries to canonicalize the given bytes. It's possible to even
 244      * canonicalize non-wellformed sequences if they are well-formed after being
 245      * wrapped with a <CODE>&gt;a&lt;...&gt;/a&lt;</CODE>.
 246      *
 247      * @param inputBytes
 248      * @return the result of the canonicalization.
 249      * @throws CanonicalizationException
 250      * @throws java.io.IOException
 251      * @throws javax.xml.parsers.ParserConfigurationException
 252      * @throws org.xml.sax.SAXException
 253      */
 254     public byte[] canonicalize(byte[] inputBytes)
 255         throws javax.xml.parsers.ParserConfigurationException,
 256         java.io.IOException, org.xml.sax.SAXException, CanonicalizationException {
 257         InputStream bais = new ByteArrayInputStream(inputBytes);
 258         InputSource in = new InputSource(bais);
 259         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
 260         dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
 261 
 262         dfactory.setNamespaceAware(true);
 263 
 264         // needs to validate for ID attribute normalization
 265         dfactory.setValidating(true);
 266 
 267         DocumentBuilder db = dfactory.newDocumentBuilder();
 268 
 269         /*
 270          * for some of the test vectors from the specification,
 271          * there has to be a validating parser for ID attributes, default
 272          * attribute values, NMTOKENS, etc.
 273          * Unfortunately, the test vectors do use different DTDs or
 274          * even no DTD. So Xerces 1.3.1 fires many warnings about using
 275          * ErrorHandlers.
 276          *
 277          * Text from the spec:
 278          *
 279          * The input octet stream MUST contain a well-formed XML document,
 280          * but the input need not be validated. However, the attribute
 281          * value normalization and entity reference resolution MUST be
 282          * performed in accordance with the behaviors of a validating
 283          * XML processor. As well, nodes for default attributes (declared
 284          * in the ATTLIST with an AttValue but not specified) are created
 285          * in each element. Thus, the declarations in the document type
 286          * declaration are used to help create the canonical form, even
 287          * though the document type declaration is not retained in the
 288          * canonical form.
 289          */
 290         db.setErrorHandler(new com.sun.org.apache.xml.internal.security.utils.IgnoreAllErrorHandler());
 291 
 292         Document document = db.parse(in);
 293         return this.canonicalizeSubtree(document);
 294     }
 295 
 296     /**
 297      * Canonicalizes the subtree rooted by <CODE>node</CODE>.
 298      *
 299      * @param node The node to canonicalize
 300      * @return the result of the c14n.
 301      *
 302      * @throws CanonicalizationException
 303      */
 304     public byte[] canonicalizeSubtree(Node node) throws CanonicalizationException {
 305         return canonicalizerSpi.engineCanonicalizeSubTree(node);
 306     }
 307 
 308     /**
 309      * Canonicalizes the subtree rooted by <CODE>node</CODE>.
 310      *
 311      * @param node
 312      * @param inclusiveNamespaces
 313      * @return the result of the c14n.
 314      * @throws CanonicalizationException
 315      */
 316     public byte[] canonicalizeSubtree(Node node, String inclusiveNamespaces)
 317         throws CanonicalizationException {
 318         return canonicalizerSpi.engineCanonicalizeSubTree(node, inclusiveNamespaces);
 319     }
 320 
 321     /**
 322      * Canonicalizes an XPath node set. The <CODE>xpathNodeSet</CODE> is treated
 323      * as a list of XPath nodes, not as a list of subtrees.
 324      *
 325      * @param xpathNodeSet
 326      * @return the result of the c14n.
 327      * @throws CanonicalizationException
 328      */
 329     public byte[] canonicalizeXPathNodeSet(NodeList xpathNodeSet)
 330         throws CanonicalizationException {
 331         return canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet);
 332     }
 333 
 334     /**
 335      * Canonicalizes an XPath node set. The <CODE>xpathNodeSet</CODE> is treated
 336      * as a list of XPath nodes, not as a list of subtrees.
 337      *
 338      * @param xpathNodeSet
 339      * @param inclusiveNamespaces
 340      * @return the result of the c14n.
 341      * @throws CanonicalizationException
 342      */
 343     public byte[] canonicalizeXPathNodeSet(
 344         NodeList xpathNodeSet, String inclusiveNamespaces
 345     ) throws CanonicalizationException {
 346         return
 347             canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, inclusiveNamespaces);
 348     }
 349 
 350     /**
 351      * Canonicalizes an XPath node set.
 352      *
 353      * @param xpathNodeSet
 354      * @return the result of the c14n.
 355      * @throws CanonicalizationException
 356      */
 357     public byte[] canonicalizeXPathNodeSet(Set<Node> xpathNodeSet)
 358         throws CanonicalizationException {
 359         return canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet);
 360     }
 361 
 362     /**
 363      * Canonicalizes an XPath node set.
 364      *
 365      * @param xpathNodeSet
 366      * @param inclusiveNamespaces
 367      * @return the result of the c14n.
 368      * @throws CanonicalizationException
 369      */
 370     public byte[] canonicalizeXPathNodeSet(
 371         Set<Node> xpathNodeSet, String inclusiveNamespaces
 372     ) throws CanonicalizationException {
 373         return
 374             canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, inclusiveNamespaces);
 375     }
 376 
 377     /**
 378      * Sets the writer where the canonicalization ends.  ByteArrayOutputStream
 379      * if none is set.
 380      * @param os
 381      */
 382     public void setWriter(OutputStream os) {
 383         canonicalizerSpi.setWriter(os);
 384     }
 385 
 386     /**
 387      * Returns the name of the implementing {@link CanonicalizerSpi} class
 388      *
 389      * @return the name of the implementing {@link CanonicalizerSpi} class
 390      */
 391     public String getImplementingCanonicalizerClass() {
 392         return canonicalizerSpi.getClass().getName();
 393     }
 394 
 395     /**
 396      * Set the canonicalizer behaviour to not reset.
 397      */
 398     public void notReset() {
 399         canonicalizerSpi.reset = false;
 400     }
 401 
 402 }