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