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.signature;
24  
25  import java.io.ByteArrayInputStream;
26  import java.io.IOException;
27  import java.io.OutputStream;
28  import javax.crypto.SecretKey;
29  import javax.crypto.spec.SecretKeySpec;
30  import javax.xml.XMLConstants;
31  import javax.xml.parsers.ParserConfigurationException;
32  
33  import com.sun.org.apache.xml.internal.security.algorithms.SignatureAlgorithm;
34  import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
35  import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
36  import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException;
37  import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
38  import com.sun.org.apache.xml.internal.security.utils.Constants;
39  import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
40  import com.sun.org.apache.xml.internal.security.transforms.params.InclusiveNamespaces;
41  import org.w3c.dom.Document;
42  import org.w3c.dom.Element;
43  import org.w3c.dom.Node;
44  import org.xml.sax.SAXException;
45  
46  /**
47   * Handles <code>&lt;ds:SignedInfo&gt;</code> elements
48   * This <code>SignedInfo<code> element includes the canonicalization algorithm,
49   * a signature algorithm, and one or more references.
50   *
51   * @author Christian Geuer-Pollmann
52   */
53  public class SignedInfo extends Manifest {
54  
55      /** Field signatureAlgorithm */
56      private SignatureAlgorithm signatureAlgorithm = null;
57  
58      /** Field c14nizedBytes           */
59      private byte[] c14nizedBytes = null;
60  
61      private Element c14nMethod;
62      private Element signatureMethod;
63  
64      /**
65       * Overwrites {@link Manifest#addDocument} because it creates another
66       * Element.
67       *
68       * @param doc the {@link Document} in which <code>XMLsignature</code> will
69       *    be placed
70       * @throws XMLSecurityException
71       */
72      public SignedInfo(Document doc) throws XMLSecurityException {
73          this(doc, XMLSignature.ALGO_ID_SIGNATURE_DSA,
74               Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
75      }
76  
77      /**
78       * Constructs {@link SignedInfo} using given Canonicalization algorithm and
79       * Signature algorithm.
80       *
81       * @param doc <code>SignedInfo</code> is placed in this document
82       * @param signatureMethodURI URI representation of the Digest and
83       *    Signature algorithm
84       * @param canonicalizationMethodURI URI representation of the
85       *    Canonicalization method
86       * @throws XMLSecurityException
87       */
88      public SignedInfo(
89          Document doc, String signatureMethodURI, String canonicalizationMethodURI
90      ) throws XMLSecurityException {
91          this(doc, signatureMethodURI, 0, canonicalizationMethodURI);
92      }
93  
94      /**
95       * Constructor SignedInfo
96       *
97       * @param doc <code>SignedInfo</code> is placed in this document
98       * @param signatureMethodURI URI representation of the Digest and
99       *    Signature algorithm
100      * @param hMACOutputLength
101      * @param canonicalizationMethodURI URI representation of the
102      *    Canonicalization method
103      * @throws XMLSecurityException
104      */
105     public SignedInfo(
106         Document doc, String signatureMethodURI,
107         int hMACOutputLength, String canonicalizationMethodURI
108     ) throws XMLSecurityException {
109         super(doc);
110 
111         c14nMethod =
112             XMLUtils.createElementInSignatureSpace(this.doc, Constants._TAG_CANONICALIZATIONMETHOD);
113 
114         c14nMethod.setAttributeNS(null, Constants._ATT_ALGORITHM, canonicalizationMethodURI);
115         this.constructionElement.appendChild(c14nMethod);
116         XMLUtils.addReturnToElement(this.constructionElement);
117 
118         if (hMACOutputLength > 0) {
119             this.signatureAlgorithm =
120                 new SignatureAlgorithm(this.doc, signatureMethodURI, hMACOutputLength);
121         } else {
122             this.signatureAlgorithm = new SignatureAlgorithm(this.doc, signatureMethodURI);
123         }
124 
125         signatureMethod = this.signatureAlgorithm.getElement();
126         this.constructionElement.appendChild(signatureMethod);
127         XMLUtils.addReturnToElement(this.constructionElement);
128     }
129 
130     /**
131      * @param doc
132      * @param signatureMethodElem
133      * @param canonicalizationMethodElem
134      * @throws XMLSecurityException
135      */
136     public SignedInfo(
137         Document doc, Element signatureMethodElem, Element canonicalizationMethodElem
138     ) throws XMLSecurityException {
139         super(doc);
140         // Check this?
141         this.c14nMethod = canonicalizationMethodElem;
142         this.constructionElement.appendChild(c14nMethod);
143         XMLUtils.addReturnToElement(this.constructionElement);
144 
145         this.signatureAlgorithm =
146             new SignatureAlgorithm(signatureMethodElem, null);
147 
148         signatureMethod = this.signatureAlgorithm.getElement();
149         this.constructionElement.appendChild(signatureMethod);
150 
151         XMLUtils.addReturnToElement(this.constructionElement);
152     }
153 
154     /**
155      * Build a {@link SignedInfo} from an {@link Element}
156      *
157      * @param element <code>SignedInfo</code>
158      * @param baseURI the URI of the resource where the XML instance was stored
159      * @throws XMLSecurityException
160      * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0033.html">
161      * Question</A>
162      * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0054.html">
163      * Answer</A>
164      */
165     public SignedInfo(Element element, String baseURI) throws XMLSecurityException {
166         this(element, baseURI, false);
167     }
168 
169     /**
170      * Build a {@link SignedInfo} from an {@link Element}
171      *
172      * @param element <code>SignedInfo</code>
173      * @param baseURI the URI of the resource where the XML instance was stored
174      * @param secureValidation whether secure validation is enabled or not
175      * @throws XMLSecurityException
176      * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0033.html">
177      * Question</A>
178      * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0054.html">
179      * Answer</A>
180      */
181     public SignedInfo(
182         Element element, String baseURI, boolean secureValidation
183     ) throws XMLSecurityException {
184         // Parse the Reference children and Id attribute in the Manifest
185         super(reparseSignedInfoElem(element), baseURI, secureValidation);
186 
187         c14nMethod = XMLUtils.getNextElement(element.getFirstChild());
188         signatureMethod = XMLUtils.getNextElement(c14nMethod.getNextSibling());
189         this.signatureAlgorithm =
190             new SignatureAlgorithm(signatureMethod, this.getBaseURI(), secureValidation);
191     }
192 
193     private static Element reparseSignedInfoElem(Element element)
194         throws XMLSecurityException {
195         /*
196          * If a custom canonicalizationMethod is used, canonicalize
197          * ds:SignedInfo, reparse it into a new document
198          * and replace the original not-canonicalized ds:SignedInfo by
199          * the re-parsed canonicalized one.
200          */
201         Element c14nMethod = XMLUtils.getNextElement(element.getFirstChild());
202         String c14nMethodURI =
203             c14nMethod.getAttributeNS(null, Constants._ATT_ALGORITHM);
204         if (!(c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS) ||
205             c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS) ||
206             c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS) ||
207             c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS) ||
208             c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS) ||
209             c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS))) {
210             // the c14n is not a secure one and can rewrite the URIs or like
211             // so reparse the SignedInfo to be sure
212             try {
213                 Canonicalizer c14nizer =
214                     Canonicalizer.getInstance(c14nMethodURI);
215 
216                 byte[] c14nizedBytes = c14nizer.canonicalizeSubtree(element);
217                 javax.xml.parsers.DocumentBuilderFactory dbf =
218                     javax.xml.parsers.DocumentBuilderFactory.newInstance();
219                 dbf.setNamespaceAware(true);
220                 dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
221                 javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
222                 Document newdoc =
223                     db.parse(new ByteArrayInputStream(c14nizedBytes));
224                 Node imported =
225                     element.getOwnerDocument().importNode(newdoc.getDocumentElement(), true);
226 
227                 element.getParentNode().replaceChild(imported, element);
228 
229                 return (Element) imported;
230             } catch (ParserConfigurationException ex) {
231                 throw new XMLSecurityException("empty", ex);
232             } catch (IOException ex) {
233                 throw new XMLSecurityException("empty", ex);
234             } catch (SAXException ex) {
235                 throw new XMLSecurityException("empty", ex);
236             }
237         }
238         return element;
239     }
240 
241     /**
242      * Tests core validation process
243      *
244      * @return true if verification was successful
245      * @throws MissingResourceFailureException
246      * @throws XMLSecurityException
247      */
248     public boolean verify()
249         throws MissingResourceFailureException, XMLSecurityException {
250         return super.verifyReferences(false);
251     }
252 
253     /**
254      * Tests core validation process
255      *
256      * @param followManifests defines whether the verification process has to verify referenced <CODE>ds:Manifest</CODE>s, too
257      * @return true if verification was successful
258      * @throws MissingResourceFailureException
259      * @throws XMLSecurityException
260      */
261     public boolean verify(boolean followManifests)
262         throws MissingResourceFailureException, XMLSecurityException {
263         return super.verifyReferences(followManifests);
264     }
265 
266     /**
267      * Returns getCanonicalizedOctetStream
268      *
269      * @return the canonicalization result octet stream of <code>SignedInfo</code> element
270      * @throws CanonicalizationException
271      * @throws InvalidCanonicalizerException
272      * @throws XMLSecurityException
273      */
274     public byte[] getCanonicalizedOctetStream()
275         throws CanonicalizationException, InvalidCanonicalizerException, XMLSecurityException {
276         if (this.c14nizedBytes == null) {
277             Canonicalizer c14nizer =
278                 Canonicalizer.getInstance(this.getCanonicalizationMethodURI());
279 
280             this.c14nizedBytes =
281                 c14nizer.canonicalizeSubtree(this.constructionElement);
282         }
283 
284         // make defensive copy
285         return this.c14nizedBytes.clone();
286     }
287 
288     /**
289      * Output the C14n stream to the given OutputStream.
290      * @param os
291      * @throws CanonicalizationException
292      * @throws InvalidCanonicalizerException
293      * @throws XMLSecurityException
294      */
295     public void signInOctetStream(OutputStream os)
296         throws CanonicalizationException, InvalidCanonicalizerException, XMLSecurityException {
297         if (this.c14nizedBytes == null) {
298             Canonicalizer c14nizer =
299                 Canonicalizer.getInstance(this.getCanonicalizationMethodURI());
300             c14nizer.setWriter(os);
301             String inclusiveNamespaces = this.getInclusiveNamespaces();
302 
303             if (inclusiveNamespaces == null) {
304                 c14nizer.canonicalizeSubtree(this.constructionElement);
305             } else {
306                 c14nizer.canonicalizeSubtree(this.constructionElement, inclusiveNamespaces);
307             }
308         } else {
309             try {
310                 os.write(this.c14nizedBytes);
311             } catch (IOException e) {
312                 throw new RuntimeException(e);
313             }
314         }
315     }
316 
317     /**
318      * Returns the Canonicalization method URI
319      *
320      * @return the Canonicalization method URI
321      */
322     public String getCanonicalizationMethodURI() {
323         return c14nMethod.getAttributeNS(null, Constants._ATT_ALGORITHM);
324     }
325 
326     /**
327      * Returns the Signature method URI
328      *
329      * @return the Signature method URI
330      */
331     public String getSignatureMethodURI() {
332         Element signatureElement = this.getSignatureMethodElement();
333 
334         if (signatureElement != null) {
335             return signatureElement.getAttributeNS(null, Constants._ATT_ALGORITHM);
336         }
337 
338         return null;
339     }
340 
341     /**
342      * Method getSignatureMethodElement
343      * @return returns the SignatureMethod Element
344      *
345      */
346     public Element getSignatureMethodElement() {
347         return signatureMethod;
348     }
349 
350     /**
351      * Creates a SecretKey for the appropriate Mac algorithm based on a
352      * byte[] array password.
353      *
354      * @param secretKeyBytes
355      * @return the secret key for the SignedInfo element.
356      */
357     public SecretKey createSecretKey(byte[] secretKeyBytes) {
358         return new SecretKeySpec(secretKeyBytes, this.signatureAlgorithm.getJCEAlgorithmString());
359     }
360 
361     protected SignatureAlgorithm getSignatureAlgorithm() {
362         return signatureAlgorithm;
363     }
364 
365     /**
366      * Method getBaseLocalName
367      * @inheritDoc
368      *
369      */
370     public String getBaseLocalName() {
371         return Constants._TAG_SIGNEDINFO;
372     }
373 
374     public String getInclusiveNamespaces() {
375         String c14nMethodURI = c14nMethod.getAttributeNS(null, Constants._ATT_ALGORITHM);
376         if (!(c14nMethodURI.equals("http://www.w3.org/2001/10/xml-exc-c14n#") ||
377             c14nMethodURI.equals("http://www.w3.org/2001/10/xml-exc-c14n#WithComments"))) {
378             return null;
379         }
380 
381         Element inclusiveElement = XMLUtils.getNextElement(c14nMethod.getFirstChild());
382 
383         if (inclusiveElement != null) {
384             try {
385                 String inclusiveNamespaces =
386                     new InclusiveNamespaces(
387                         inclusiveElement,
388                         InclusiveNamespaces.ExclusiveCanonicalizationNamespace
389                     ).getInclusiveNamespaces();
390                 return inclusiveNamespaces;
391             } catch (XMLSecurityException e) {
392                 return null;
393             }
394         }
395         return null;
396     }
397 }