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