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.IOException;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import javax.xml.parsers.ParserConfigurationException;
34  
35  import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
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.transforms.Transforms;
39  import com.sun.org.apache.xml.internal.security.utils.Constants;
40  import com.sun.org.apache.xml.internal.security.utils.I18n;
41  import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy;
42  import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
43  import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver;
44  import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi;
45  import org.w3c.dom.Attr;
46  import org.w3c.dom.DOMException;
47  import org.w3c.dom.Document;
48  import org.w3c.dom.Element;
49  import org.w3c.dom.Node;
50  import org.xml.sax.SAXException;
51  
52  /**
53   * Handles <code>&lt;ds:Manifest&gt;</code> elements.
54   * <p> This element holds the <code>Reference</code> elements</p>
55   */
56  public class Manifest extends SignatureElementProxy {
57  
58      /**
59       * The maximum number of references per Manifest, if secure validation is enabled.
60       */
61      public static final int MAXIMUM_REFERENCE_COUNT = 30;
62  
63      /** {@link org.apache.commons.logging} logging facility */
64      private static java.util.logging.Logger log =
65          java.util.logging.Logger.getLogger(Manifest.class.getName());
66  
67      /** Field references */
68      private List<Reference> references;
69      private Element[] referencesEl;
70  
71      /** Field verificationResults[] */
72      private boolean verificationResults[] = null;
73  
74      /** Field resolverProperties */
75      private Map<String, String> resolverProperties = null;
76  
77      /** Field perManifestResolvers */
78      private List<ResourceResolver> perManifestResolvers = null;
79  
80      private boolean secureValidation;
81  
82      /**
83       * Constructs {@link Manifest}
84       *
85       * @param doc the {@link Document} in which <code>XMLsignature</code> is placed
86       */
87      public Manifest(Document doc) {
88          super(doc);
89  
90          XMLUtils.addReturnToElement(this.constructionElement);
91  
92          this.references = new ArrayList<Reference>();
93      }
94  
95      /**
96       * Constructor Manifest
97       *
98       * @param element
99       * @param baseURI
100      * @throws XMLSecurityException
101      */
102     public Manifest(Element element, String baseURI) throws XMLSecurityException {
103         this(element, baseURI, false);
104 
105     }
106     /**
107      * Constructor Manifest
108      *
109      * @param element
110      * @param baseURI
111      * @param secureValidation
112      * @throws XMLSecurityException
113      */
114     public Manifest(
115         Element element, String baseURI, boolean secureValidation
116     ) throws XMLSecurityException {
117         super(element, baseURI);
118 
119         Attr attr = element.getAttributeNodeNS(null, "Id");
120         if (attr != null) {
121             element.setIdAttributeNode(attr, true);
122         }
123         this.secureValidation = secureValidation;
124 
125         // check out Reference children
126         this.referencesEl =
127             XMLUtils.selectDsNodes(
128                 this.constructionElement.getFirstChild(), Constants._TAG_REFERENCE
129             );
130         int le = this.referencesEl.length;
131         if (le == 0) {
132             // At least one Reference must be present. Bad.
133             Object exArgs[] = { Constants._TAG_REFERENCE, Constants._TAG_MANIFEST };
134 
135             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
136                                    I18n.translate("xml.WrongContent", exArgs));
137         }
138 
139         if (secureValidation && le > MAXIMUM_REFERENCE_COUNT) {
140             Object exArgs[] = { le, MAXIMUM_REFERENCE_COUNT };
141 
142             throw new XMLSecurityException("signature.tooManyReferences", exArgs);
143         }
144 
145         // create List
146         this.references = new ArrayList<Reference>(le);
147 
148         for (int i = 0; i < le; i++) {
149             Element refElem = referencesEl[i];
150             Attr refAttr = refElem.getAttributeNodeNS(null, "Id");
151             if (refAttr != null) {
152                 refElem.setIdAttributeNode(refAttr, true);
153             }
154             this.references.add(null);
155         }
156     }
157 
158     /**
159      * This <code>addDocument</code> method is used to add a new resource to the
160      * signed info. A {@link com.sun.org.apache.xml.internal.security.signature.Reference} is built
161      * from the supplied values.
162      *
163      * @param baseURI the URI of the resource where the XML instance was stored
164      * @param referenceURI <code>URI</code> attribute in <code>Reference</code> for specifying
165      * where data is
166      * @param transforms com.sun.org.apache.xml.internal.security.signature.Transforms object with an ordered
167      * list of transformations to be performed.
168      * @param digestURI The digest algorithm URI to be used.
169      * @param referenceId
170      * @param referenceType
171      * @throws XMLSignatureException
172      */
173     public void addDocument(
174         String baseURI, String referenceURI, Transforms transforms,
175         String digestURI, String referenceId, String referenceType
176     ) throws XMLSignatureException {
177         // the this.doc is handed implicitly by the this.getOwnerDocument()
178         Reference ref =
179             new Reference(this.doc, baseURI, referenceURI, this, transforms, digestURI);
180 
181         if (referenceId != null) {
182             ref.setId(referenceId);
183         }
184 
185         if (referenceType != null) {
186             ref.setType(referenceType);
187         }
188 
189         // add Reference object to our cache vector
190         this.references.add(ref);
191 
192         // add the Element of the Reference object to the Manifest/SignedInfo
193         this.constructionElement.appendChild(ref.getElement());
194         XMLUtils.addReturnToElement(this.constructionElement);
195     }
196 
197     /**
198      * The calculation of the DigestValues in the References must be after the
199      * References are already added to the document and during the signing
200      * process. This ensures that all necessary data is in place.
201      *
202      * @throws ReferenceNotInitializedException
203      * @throws XMLSignatureException
204      */
205     public void generateDigestValues()
206         throws XMLSignatureException, ReferenceNotInitializedException {
207         for (int i = 0; i < this.getLength(); i++) {
208             // update the cached Reference object, the Element content is automatically updated
209             Reference currentRef = this.references.get(i);
210             currentRef.generateDigestValue();
211         }
212     }
213 
214     /**
215      * Return the nonnegative number of added references.
216      *
217      * @return the number of references
218      */
219     public int getLength() {
220         return this.references.size();
221     }
222 
223     /**
224      * Return the <it>i</it><sup>th</sup> reference. Valid <code>i</code>
225      * values are 0 to <code>{link@ getSize}-1</code>.
226      *
227      * @param i Index of the requested {@link Reference}
228      * @return the <it>i</it><sup>th</sup> reference
229      * @throws XMLSecurityException
230      */
231     public Reference item(int i) throws XMLSecurityException {
232         if (this.references.get(i) == null) {
233             // not yet constructed, so _we_ have to
234             Reference ref =
235                 new Reference(referencesEl[i], this.baseURI, this, secureValidation);
236 
237             this.references.set(i, ref);
238         }
239 
240         return this.references.get(i);
241     }
242 
243     /**
244      * Sets the <code>Id</code> attribute
245      *
246      * @param Id the <code>Id</code> attribute in <code>ds:Manifest</code>
247      */
248     public void setId(String Id) {
249         if (Id != null) {
250             this.constructionElement.setAttributeNS(null, Constants._ATT_ID, Id);
251             this.constructionElement.setIdAttributeNS(null, Constants._ATT_ID, true);
252         }
253     }
254 
255     /**
256      * Returns the <code>Id</code> attribute
257      *
258      * @return the <code>Id</code> attribute in <code>ds:Manifest</code>
259      */
260     public String getId() {
261         return this.constructionElement.getAttributeNS(null, Constants._ATT_ID);
262     }
263 
264     /**
265      * Used to do a <A HREF="http://www.w3.org/TR/xmldsig-core/#def-ValidationReference">reference
266      * validation</A> of all enclosed references using the {@link Reference#verify} method.
267      *
268      * <p>This step loops through all {@link Reference}s and does verify the hash
269      * values. If one or more verifications fail, the method returns
270      * <code>false</code>. If <i>all</i> verifications are successful,
271      * it returns <code>true</code>. The results of the individual reference
272      * validations are available by using the {@link #getVerificationResult(int)} method
273      *
274      * @return true if all References verify, false if one or more do not verify.
275      * @throws MissingResourceFailureException if a {@link Reference} does not verify
276      * (throws a {@link com.sun.org.apache.xml.internal.security.signature.ReferenceNotInitializedException}
277      * because of an uninitialized {@link XMLSignatureInput}
278      * @see com.sun.org.apache.xml.internal.security.signature.Reference#verify
279      * @see com.sun.org.apache.xml.internal.security.signature.SignedInfo#verify()
280      * @see com.sun.org.apache.xml.internal.security.signature.MissingResourceFailureException
281      * @throws XMLSecurityException
282      */
283     public boolean verifyReferences()
284         throws MissingResourceFailureException, XMLSecurityException {
285         return this.verifyReferences(false);
286     }
287 
288     /**
289      * Used to do a <A HREF="http://www.w3.org/TR/xmldsig-core/#def-ValidationReference">reference
290      * validation</A> of all enclosed references using the {@link Reference#verify} method.
291      *
292      * <p>This step loops through all {@link Reference}s and does verify the hash
293      * values. If one or more verifications fail, the method returns
294      * <code>false</code>. If <i>all</i> verifications are successful,
295      * it returns <code>true</code>. The results of the individual reference
296      * validations are available by using the {@link #getVerificationResult(int)} method
297      *
298      * @param followManifests
299      * @return true if all References verify, false if one or more do not verify.
300      * @throws MissingResourceFailureException if a {@link Reference} does not verify
301      * (throws a {@link com.sun.org.apache.xml.internal.security.signature.ReferenceNotInitializedException}
302      * because of an uninitialized {@link XMLSignatureInput}
303      * @see com.sun.org.apache.xml.internal.security.signature.Reference#verify
304      * @see com.sun.org.apache.xml.internal.security.signature.SignedInfo#verify(boolean)
305      * @see com.sun.org.apache.xml.internal.security.signature.MissingResourceFailureException
306      * @throws XMLSecurityException
307      */
308     public boolean verifyReferences(boolean followManifests)
309         throws MissingResourceFailureException, XMLSecurityException {
310         if (referencesEl == null) {
311             this.referencesEl =
312                 XMLUtils.selectDsNodes(
313                     this.constructionElement.getFirstChild(), Constants._TAG_REFERENCE
314                 );
315         }
316         if (log.isLoggable(java.util.logging.Level.FINE)) {
317             log.log(java.util.logging.Level.FINE, "verify " + referencesEl.length + " References");
318             log.log(java.util.logging.Level.FINE, "I am " + (followManifests
319                 ? "" : "not") + " requested to follow nested Manifests");
320         }
321         if (referencesEl.length == 0) {
322             throw new XMLSecurityException("empty");
323         }
324         if (secureValidation && referencesEl.length > MAXIMUM_REFERENCE_COUNT) {
325             Object exArgs[] = { referencesEl.length, MAXIMUM_REFERENCE_COUNT };
326 
327             throw new XMLSecurityException("signature.tooManyReferences", exArgs);
328         }
329 
330         this.verificationResults = new boolean[referencesEl.length];
331         boolean verify = true;
332         for (int i = 0; i < this.referencesEl.length; i++) {
333             Reference currentRef =
334                 new Reference(referencesEl[i], this.baseURI, this, secureValidation);
335 
336             this.references.set(i, currentRef);
337 
338             // if only one item does not verify, the whole verification fails
339             try {
340                 boolean currentRefVerified = currentRef.verify();
341 
342                 this.setVerificationResult(i, currentRefVerified);
343 
344                 if (!currentRefVerified) {
345                     verify = false;
346                 }
347                 if (log.isLoggable(java.util.logging.Level.FINE)) {
348                     log.log(java.util.logging.Level.FINE, "The Reference has Type " + currentRef.getType());
349                 }
350 
351                 // was verification successful till now and do we want to verify the Manifest?
352                 if (verify && followManifests && currentRef.typeIsReferenceToManifest()) {
353                     if (log.isLoggable(java.util.logging.Level.FINE)) {
354                         log.log(java.util.logging.Level.FINE, "We have to follow a nested Manifest");
355                     }
356 
357                     try {
358                         XMLSignatureInput signedManifestNodes =
359                             currentRef.dereferenceURIandPerformTransforms(null);
360                         Set<Node> nl = signedManifestNodes.getNodeSet();
361                         Manifest referencedManifest = null;
362                         Iterator<Node> nlIterator = nl.iterator();
363 
364                         findManifest: while (nlIterator.hasNext()) {
365                             Node n = nlIterator.next();
366 
367                             if ((n.getNodeType() == Node.ELEMENT_NODE)
368                                 && ((Element) n).getNamespaceURI().equals(Constants.SignatureSpecNS)
369                                 && ((Element) n).getLocalName().equals(Constants._TAG_MANIFEST)
370                             ) {
371                                 try {
372                                     referencedManifest =
373                                         new Manifest(
374                                              (Element)n, signedManifestNodes.getSourceURI(), secureValidation
375                                         );
376                                     break findManifest;
377                                 } catch (XMLSecurityException ex) {
378                                     if (log.isLoggable(java.util.logging.Level.FINE)) {
379                                         log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
380                                     }
381                                     // Hm, seems not to be a ds:Manifest
382                                 }
383                             }
384                         }
385 
386                         if (referencedManifest == null) {
387                             // The Reference stated that it points to a ds:Manifest
388                             // but we did not find a ds:Manifest in the signed area
389                             throw new MissingResourceFailureException("empty", currentRef);
390                         }
391 
392                         referencedManifest.perManifestResolvers = this.perManifestResolvers;
393                         referencedManifest.resolverProperties = this.resolverProperties;
394 
395                         boolean referencedManifestValid =
396                             referencedManifest.verifyReferences(followManifests);
397 
398                         if (!referencedManifestValid) {
399                             verify = false;
400 
401                             log.log(java.util.logging.Level.WARNING, "The nested Manifest was invalid (bad)");
402                         } else {
403                             if (log.isLoggable(java.util.logging.Level.FINE)) {
404                                 log.log(java.util.logging.Level.FINE, "The nested Manifest was valid (good)");
405                             }
406                         }
407                     } catch (IOException ex) {
408                         throw new ReferenceNotInitializedException("empty", ex);
409                     } catch (ParserConfigurationException ex) {
410                         throw new ReferenceNotInitializedException("empty", ex);
411                     } catch (SAXException ex) {
412                         throw new ReferenceNotInitializedException("empty", ex);
413                     }
414                 }
415             } catch (ReferenceNotInitializedException ex) {
416                 Object exArgs[] = { currentRef.getURI() };
417 
418                 throw new MissingResourceFailureException(
419                     "signature.Verification.Reference.NoInput", exArgs, ex, currentRef
420                 );
421             }
422         }
423 
424         return verify;
425     }
426 
427     /**
428      * Method setVerificationResult
429      *
430      * @param index
431      * @param verify
432      */
433     private void setVerificationResult(int index, boolean verify) {
434         if (this.verificationResults == null) {
435             this.verificationResults = new boolean[this.getLength()];
436         }
437 
438         this.verificationResults[index] = verify;
439     }
440 
441     /**
442      * After verifying a {@link Manifest} or a {@link SignedInfo} using the
443      * {@link Manifest#verifyReferences()} or {@link SignedInfo#verify()} methods,
444      * the individual results can be retrieved with this method.
445      *
446      * @param index an index of into a {@link Manifest} or a {@link SignedInfo}
447      * @return the results of reference validation at the specified index
448      * @throws XMLSecurityException
449      */
450     public boolean getVerificationResult(int index) throws XMLSecurityException {
451         if ((index < 0) || (index > this.getLength() - 1)) {
452             Object exArgs[] = { Integer.toString(index), Integer.toString(this.getLength()) };
453             Exception e =
454                 new IndexOutOfBoundsException(
455                     I18n.translate("signature.Verification.IndexOutOfBounds", exArgs)
456                 );
457 
458             throw new XMLSecurityException("generic.EmptyMessage", e);
459         }
460 
461         if (this.verificationResults == null) {
462             try {
463                 this.verifyReferences();
464             } catch (Exception ex) {
465                 throw new XMLSecurityException("generic.EmptyMessage", ex);
466             }
467         }
468 
469         return this.verificationResults[index];
470     }
471 
472     /**
473      * Adds Resource Resolver for retrieving resources at specified <code>URI</code> attribute
474      * in <code>reference</code> element
475      *
476      * @param resolver {@link ResourceResolver} can provide the implemenatin subclass of
477      * {@link ResourceResolverSpi} for retrieving resource.
478      */
479     public void addResourceResolver(ResourceResolver resolver) {
480         if (resolver == null) {
481             return;
482         }
483         if (perManifestResolvers == null) {
484             perManifestResolvers = new ArrayList<ResourceResolver>();
485         }
486         this.perManifestResolvers.add(resolver);
487     }
488 
489     /**
490      * Adds Resource Resolver for retrieving resources at specified <code>URI</code> attribute
491      * in <code>reference</code> element
492      *
493      * @param resolverSpi the implementation subclass of {@link ResourceResolverSpi} for
494      * retrieving the resource.
495      */
496     public void addResourceResolver(ResourceResolverSpi resolverSpi) {
497         if (resolverSpi == null) {
498             return;
499         }
500         if (perManifestResolvers == null) {
501             perManifestResolvers = new ArrayList<ResourceResolver>();
502         }
503         perManifestResolvers.add(new ResourceResolver(resolverSpi));
504     }
505 
506     /**
507      * Get the Per-Manifest Resolver List
508      * @return the per-manifest Resolver List
509      */
510     public List<ResourceResolver> getPerManifestResolvers() {
511         return perManifestResolvers;
512     }
513 
514     /**
515      * Get the resolver property map
516      * @return the resolver property map
517      */
518     public Map<String, String> getResolverProperties() {
519         return resolverProperties;
520     }
521 
522     /**
523      * Used to pass parameters like proxy servers etc to the ResourceResolver
524      * implementation.
525      *
526      * @param key the key
527      * @param value the value
528      */
529     public void setResolverProperty(String key, String value) {
530         if (resolverProperties == null) {
531             resolverProperties = new HashMap<String, String>(10);
532         }
533         this.resolverProperties.put(key, value);
534     }
535 
536     /**
537      * Returns the value at specified key
538      *
539      * @param key the key
540      * @return the value
541      */
542     public String getResolverProperty(String key) {
543         return this.resolverProperties.get(key);
544     }
545 
546     /**
547      * Method getSignedContentItem
548      *
549      * @param i
550      * @return The signed content of the i reference.
551      *
552      * @throws XMLSignatureException
553      */
554     public byte[] getSignedContentItem(int i) throws XMLSignatureException {
555         try {
556             return this.getReferencedContentAfterTransformsItem(i).getBytes();
557         } catch (IOException ex) {
558             throw new XMLSignatureException("empty", ex);
559         } catch (CanonicalizationException ex) {
560             throw new XMLSignatureException("empty", ex);
561         } catch (InvalidCanonicalizerException ex) {
562             throw new XMLSignatureException("empty", ex);
563         } catch (XMLSecurityException ex) {
564             throw new XMLSignatureException("empty", ex);
565         }
566     }
567 
568     /**
569      * Method getReferencedContentPriorTransformsItem
570      *
571      * @param i
572      * @return The contents before transformation of the reference i.
573      * @throws XMLSecurityException
574      */
575     public XMLSignatureInput getReferencedContentBeforeTransformsItem(int i)
576         throws XMLSecurityException {
577         return this.item(i).getContentsBeforeTransformation();
578     }
579 
580     /**
581      * Method getReferencedContentAfterTransformsItem
582      *
583      * @param i
584      * @return The contents after transformation of the reference i.
585      * @throws XMLSecurityException
586      */
587     public XMLSignatureInput getReferencedContentAfterTransformsItem(int i)
588         throws XMLSecurityException {
589         return this.item(i).getContentsAfterTransformation();
590     }
591 
592     /**
593      * Method getSignedContentLength
594      *
595      * @return The number of references contained in this reference.
596      */
597     public int getSignedContentLength() {
598         return this.getLength();
599     }
600 
601     /**
602      * Method getBaseLocalName
603      *
604      * @inheritDoc
605      */
606     public String getBaseLocalName() {
607         return Constants._TAG_MANIFEST;
608     }
609 }