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.transforms;
24
25import java.io.IOException;
26import java.io.OutputStream;
27
28import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
29import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
30import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException;
31import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
32import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException;
33import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
34import com.sun.org.apache.xml.internal.security.utils.Constants;
35import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy;
36import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
37import org.w3c.dom.DOMException;
38import org.w3c.dom.Document;
39import org.w3c.dom.Element;
40import org.w3c.dom.NodeList;
41
42/**
43 * Holder of the {@link com.sun.org.apache.xml.internal.security.transforms.Transform} steps to
44 * be performed on the data.
45 * The input to the first Transform is the result of dereferencing the
46 * <code>URI</code> attribute of the <code>Reference</code> element.
47 * The output from the last Transform is the input for the
48 * <code>DigestMethod algorithm</code>
49 *
50 * @author Christian Geuer-Pollmann
51 * @see Transform
52 * @see com.sun.org.apache.xml.internal.security.signature.Reference
53 */
54public class Transforms extends SignatureElementProxy {
55
56    /** Canonicalization - Required Canonical XML (omits comments) */
57    public static final String TRANSFORM_C14N_OMIT_COMMENTS
58        = Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS;
59
60    /** Canonicalization - Recommended Canonical XML with Comments */
61    public static final String TRANSFORM_C14N_WITH_COMMENTS
62        = Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS;
63
64    /** Canonicalization - Required Canonical XML 1.1 (omits comments) */
65    public static final String TRANSFORM_C14N11_OMIT_COMMENTS
66        = Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS;
67
68    /** Canonicalization - Recommended Canonical XML 1.1 with Comments */
69    public static final String TRANSFORM_C14N11_WITH_COMMENTS
70        = Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS;
71
72    /** Canonicalization - Required Exclusive Canonicalization (omits comments) */
73    public static final String TRANSFORM_C14N_EXCL_OMIT_COMMENTS
74        = Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
75
76    /** Canonicalization - Recommended Exclusive Canonicalization with Comments */
77    public static final String TRANSFORM_C14N_EXCL_WITH_COMMENTS
78        = Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS;
79
80    /** Transform - Optional XSLT */
81    public static final String TRANSFORM_XSLT
82        = "http://www.w3.org/TR/1999/REC-xslt-19991116";
83
84    /** Transform - Required base64 decoding */
85    public static final String TRANSFORM_BASE64_DECODE
86        = Constants.SignatureSpecNS + "base64";
87
88    /** Transform - Recommended XPath */
89    public static final String TRANSFORM_XPATH
90        = "http://www.w3.org/TR/1999/REC-xpath-19991116";
91
92    /** Transform - Required Enveloped Signature */
93    public static final String TRANSFORM_ENVELOPED_SIGNATURE
94        = Constants.SignatureSpecNS + "enveloped-signature";
95
96    /** Transform - XPointer */
97    public static final String TRANSFORM_XPOINTER
98        = "http://www.w3.org/TR/2001/WD-xptr-20010108";
99
100    /** Transform - XPath Filter */
101    public static final String TRANSFORM_XPATH2FILTER
102        = "http://www.w3.org/2002/06/xmldsig-filter2";
103
104    /** {@link org.apache.commons.logging} logging facility */
105    private static java.util.logging.Logger log =
106        java.util.logging.Logger.getLogger(Transforms.class.getName());
107
108    private Element[] transforms;
109
110    protected Transforms() { };
111
112    private boolean secureValidation;
113
114    /**
115     * Constructs {@link Transforms}.
116     *
117     * @param doc the {@link Document} in which <code>XMLSignature</code> will
118     * be placed
119     */
120    public Transforms(Document doc) {
121        super(doc);
122        XMLUtils.addReturnToElement(this.constructionElement);
123    }
124
125    /**
126     * Constructs {@link Transforms} from {@link Element} which is
127     * <code>Transforms</code> Element
128     *
129     * @param element  is <code>Transforms</code> element
130     * @param BaseURI the URI where the XML instance was stored
131     * @throws DOMException
132     * @throws InvalidTransformException
133     * @throws TransformationException
134     * @throws XMLSecurityException
135     * @throws XMLSignatureException
136     */
137    public Transforms(Element element, String BaseURI)
138        throws DOMException, XMLSignatureException, InvalidTransformException,
139            TransformationException, XMLSecurityException {
140        super(element, BaseURI);
141
142        int numberOfTransformElems = this.getLength();
143
144        if (numberOfTransformElems == 0) {
145            // At least one Transform element must be present. Bad.
146            Object exArgs[] = { Constants._TAG_TRANSFORM, Constants._TAG_TRANSFORMS };
147
148            throw new TransformationException("xml.WrongContent", exArgs);
149        }
150    }
151
152    /**
153     * Set whether secure validation is enabled or not. The default is false.
154     */
155    public void setSecureValidation(boolean secureValidation) {
156        this.secureValidation = secureValidation;
157    }
158
159    /**
160     * Adds the <code>Transform</code> with the specified <code>Transform
161     * algorithm URI</code>
162     *
163     * @param transformURI the URI form of transform that indicates which
164     * transformation is applied to data
165     * @throws TransformationException
166     */
167    public void addTransform(String transformURI) throws TransformationException {
168        try {
169            if (log.isLoggable(java.util.logging.Level.FINE)) {
170                log.log(java.util.logging.Level.FINE, "Transforms.addTransform(" + transformURI + ")");
171            }
172
173            Transform transform = new Transform(this.doc, transformURI);
174
175            this.addTransform(transform);
176        } catch (InvalidTransformException ex) {
177            throw new TransformationException("empty", ex);
178        }
179    }
180
181    /**
182     * Adds the <code>Transform</code> with the specified <code>Transform
183     * algorithm URI</code>
184     *
185     * @param transformURI the URI form of transform that indicates which
186     * transformation is applied to data
187     * @param contextElement
188     * @throws TransformationException
189     */
190    public void addTransform(String transformURI, Element contextElement)
191       throws TransformationException {
192        try {
193            if (log.isLoggable(java.util.logging.Level.FINE)) {
194                log.log(java.util.logging.Level.FINE, "Transforms.addTransform(" + transformURI + ")");
195            }
196
197            Transform transform = new Transform(this.doc, transformURI, contextElement);
198
199            this.addTransform(transform);
200        } catch (InvalidTransformException ex) {
201            throw new TransformationException("empty", ex);
202        }
203    }
204
205    /**
206     * Adds the <code>Transform</code> with the specified <code>Transform
207     * algorithm URI</code>.
208     *
209     * @param transformURI the URI form of transform that indicates which
210     * transformation is applied to data
211     * @param contextNodes
212     * @throws TransformationException
213     */
214    public void addTransform(String transformURI, NodeList contextNodes)
215       throws TransformationException {
216
217        try {
218            Transform transform = new Transform(this.doc, transformURI, contextNodes);
219            this.addTransform(transform);
220        } catch (InvalidTransformException ex) {
221            throw new TransformationException("empty", ex);
222        }
223    }
224
225    /**
226     * Adds a user-provided Transform step.
227     *
228     * @param transform {@link Transform} object
229     */
230    private void addTransform(Transform transform) {
231        if (log.isLoggable(java.util.logging.Level.FINE)) {
232            log.log(java.util.logging.Level.FINE, "Transforms.addTransform(" + transform.getURI() + ")");
233        }
234
235        Element transformElement = transform.getElement();
236
237        this.constructionElement.appendChild(transformElement);
238        XMLUtils.addReturnToElement(this.constructionElement);
239    }
240
241    /**
242     * Applies all included <code>Transform</code>s to xmlSignatureInput and
243     * returns the result of these transformations.
244     *
245     * @param xmlSignatureInput the input for the <code>Transform</code>s
246     * @return the result of the <code>Transforms</code>
247     * @throws TransformationException
248     */
249    public XMLSignatureInput performTransforms(
250        XMLSignatureInput xmlSignatureInput
251    ) throws TransformationException {
252        return performTransforms(xmlSignatureInput, null);
253    }
254
255    /**
256     * Applies all included <code>Transform</code>s to xmlSignatureInput and
257     * returns the result of these transformations.
258     *
259     * @param xmlSignatureInput the input for the <code>Transform</code>s
260     * @param os where to output the last transformation.
261     * @return the result of the <code>Transforms</code>
262     * @throws TransformationException
263     */
264    public XMLSignatureInput performTransforms(
265        XMLSignatureInput xmlSignatureInput, OutputStream os
266    ) throws TransformationException {
267        try {
268            int last = this.getLength() - 1;
269            for (int i = 0; i < last; i++) {
270                Transform t = this.item(i);
271                String uri = t.getURI();
272                if (log.isLoggable(java.util.logging.Level.FINE)) {
273                    log.log(java.util.logging.Level.FINE, "Perform the (" + i + ")th " + uri + " transform");
274                }
275                checkSecureValidation(t);
276                xmlSignatureInput = t.performTransform(xmlSignatureInput);
277            }
278            if (last >= 0) {
279                Transform t = this.item(last);
280                checkSecureValidation(t);
281                xmlSignatureInput = t.performTransform(xmlSignatureInput, os);
282            }
283
284            return xmlSignatureInput;
285        } catch (IOException ex) {
286            throw new TransformationException("empty", ex);
287        } catch (CanonicalizationException ex) {
288            throw new TransformationException("empty", ex);
289        } catch (InvalidCanonicalizerException ex) {
290            throw new TransformationException("empty", ex);
291        }
292    }
293
294    private void checkSecureValidation(Transform transform) throws TransformationException {
295        String uri = transform.getURI();
296        if (secureValidation && Transforms.TRANSFORM_XSLT.equals(uri)) {
297            Object exArgs[] = { uri };
298
299            throw new TransformationException(
300                "signature.Transform.ForbiddenTransform", exArgs
301            );
302        }
303    }
304
305    /**
306     * Return the nonnegative number of transformations.
307     *
308     * @return the number of transformations
309     */
310    public int getLength() {
311        if (transforms == null) {
312            transforms =
313                XMLUtils.selectDsNodes(this.constructionElement.getFirstChild(), "Transform");
314        }
315        return transforms.length;
316    }
317
318    /**
319     * Return the <it>i</it><sup>th</sup> <code>{@link Transform}</code>.
320     * Valid <code>i</code> values are 0 to <code>{@link #getLength}-1</code>.
321     *
322     * @param i index of {@link Transform} to return
323     * @return the <it>i</it><sup>th</sup> Transform
324     * @throws TransformationException
325     */
326    public Transform item(int i) throws TransformationException {
327        try {
328            if (transforms == null) {
329                transforms =
330                    XMLUtils.selectDsNodes(this.constructionElement.getFirstChild(), "Transform");
331            }
332            return new Transform(transforms[i], this.baseURI);
333        } catch (XMLSecurityException ex) {
334            throw new TransformationException("empty", ex);
335        }
336    }
337
338    /** @inheritDoc */
339    public String getBaseLocalName() {
340        return Constants._TAG_TRANSFORMS;
341    }
342}
343