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 */
23package com.sun.org.apache.xml.internal.security.c14n;
24
25import java.io.ByteArrayInputStream;
26import java.io.InputStream;
27import java.io.OutputStream;
28import java.util.Map;
29import java.util.Set;
30import java.util.concurrent.ConcurrentHashMap;
31
32import javax.xml.XMLConstants;
33import javax.xml.parsers.DocumentBuilder;
34import javax.xml.parsers.DocumentBuilderFactory;
35
36import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer11_OmitComments;
37import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer11_WithComments;
38import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315ExclOmitComments;
39import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315ExclWithComments;
40import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315OmitComments;
41import com.sun.org.apache.xml.internal.security.c14n.implementations.Canonicalizer20010315WithComments;
42import com.sun.org.apache.xml.internal.security.c14n.implementations.CanonicalizerPhysical;
43import com.sun.org.apache.xml.internal.security.exceptions.AlgorithmAlreadyRegisteredException;
44import com.sun.org.apache.xml.internal.security.utils.JavaUtils;
45import org.w3c.dom.Document;
46import org.w3c.dom.Node;
47import org.w3c.dom.NodeList;
48import org.xml.sax.InputSource;
49
50/**
51 *
52 * @author Christian Geuer-Pollmann
53 */
54public 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}
405