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