1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2005 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 /*
  22  * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 /*
  25  * ===========================================================================
  26  *
  27  * (C) Copyright IBM Corp. 2003 All Rights Reserved.
  28  *
  29  * ===========================================================================
  30  */
  31 /*
  32  * $Id: DOMRetrievalMethod.java,v 1.2 2008/07/24 15:20:32 mullan Exp $
  33  */
  34 package org.jcp.xml.dsig.internal.dom;
  35 
  36 import java.io.ByteArrayInputStream;
  37 import java.net.URI;
  38 import java.net.URISyntaxException;
  39 import java.security.Provider;
  40 import java.util.*;
  41 import javax.xml.XMLConstants;
  42 import javax.xml.crypto.*;
  43 import javax.xml.crypto.dsig.*;
  44 import javax.xml.crypto.dom.DOMCryptoContext;
  45 import javax.xml.crypto.dom.DOMURIReference;
  46 import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
  47 import javax.xml.parsers.*;
  48 import org.w3c.dom.Attr;
  49 import org.w3c.dom.Document;
  50 import org.w3c.dom.Element;
  51 import org.w3c.dom.Node;
  52 
  53 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
  54 
  55 /**
  56  * DOM-based implementation of RetrievalMethod.
  57  *
  58  * @author Sean Mullan
  59  * @author Joyce Leung
  60  */
  61 public final class DOMRetrievalMethod extends DOMStructure
  62     implements RetrievalMethod, DOMURIReference {
  63 
  64     private final List transforms;
  65     private String uri;
  66     private String type;
  67     private Attr here;
  68 
  69     /**
  70      * Creates a <code>DOMRetrievalMethod</code> containing the specified
  71      * URIReference and List of Transforms.
  72      *
  73      * @param uri the URI
  74      * @param type the type
  75      * @param transforms a list of {@link Transform}s. The list is defensively
  76      *    copied to prevent subsequent modification. May be <code>null</code>
  77      *    or empty.
  78      * @throws IllegalArgumentException if the format of <code>uri</code> is
  79      *    invalid, as specified by Reference's URI attribute in the W3C
  80      *    specification for XML-Signature Syntax and Processing
  81      * @throws NullPointerException if <code>uriReference</code>
  82      *    is <code>null</code>
  83      * @throws ClassCastException if <code>transforms</code> contains any
  84      *    entries that are not of type {@link Transform}
  85      */
  86     public DOMRetrievalMethod(String uri, String type, List transforms) {
  87         if (uri == null) {
  88             throw new NullPointerException("uri cannot be null");
  89         }
  90         if (transforms == null || transforms.isEmpty()) {
  91             this.transforms = Collections.EMPTY_LIST;
  92         } else {
  93             List transformsCopy = new ArrayList(transforms);
  94             for (int i = 0, size = transformsCopy.size(); i < size; i++) {
  95                 if (!(transformsCopy.get(i) instanceof Transform)) {
  96                     throw new ClassCastException
  97                         ("transforms["+i+"] is not a valid type");
  98                 }
  99             }
 100             this.transforms = Collections.unmodifiableList(transformsCopy);
 101         }
 102         this.uri = uri;
 103         if ((uri != null) && (!uri.equals(""))) {
 104             try {
 105                 new URI(uri);
 106             } catch (URISyntaxException e) {
 107                 throw new IllegalArgumentException(e.getMessage());
 108             }
 109         }
 110 
 111         this.type = type;
 112     }
 113 
 114     /**
 115      * Creates a <code>DOMRetrievalMethod</code> from an element.
 116      *
 117      * @param rmElem a RetrievalMethod element
 118      */
 119     public DOMRetrievalMethod(Element rmElem, XMLCryptoContext context,
 120         Provider provider) throws MarshalException {
 121         // get URI and Type attributes
 122         uri = DOMUtils.getAttributeValue(rmElem, "URI");
 123         type = DOMUtils.getAttributeValue(rmElem, "Type");
 124 
 125         // get here node
 126         here = rmElem.getAttributeNodeNS(null, "URI");
 127 
 128         boolean secVal = Utils.secureValidation(context);
 129 
 130         // get Transforms, if specified
 131         List transforms = new ArrayList();
 132         Element transformsElem = DOMUtils.getFirstChildElement(rmElem);
 133 
 134         int transformCount = 0;
 135         if (transformsElem != null) {
 136             Element transformElem =
 137                 DOMUtils.getFirstChildElement(transformsElem);
 138             while (transformElem != null) {
 139                 transforms.add
 140                     (new DOMTransform(transformElem, context, provider));
 141                 transformElem = DOMUtils.getNextSiblingElement(transformElem);
 142 
 143                 transformCount++;
 144                 if (secVal &&
 145                     (transformCount > DOMReference.MAXIMUM_TRANSFORM_COUNT))
 146                 {
 147                     String error = "A maxiumum of " +
 148                                    DOMReference.MAXIMUM_TRANSFORM_COUNT +
 149                                    " transforms per Reference are allowed" +
 150                                    " with secure validation";
 151                     throw new MarshalException(error);
 152                 }
 153             }
 154         }
 155         if (transforms.isEmpty()) {
 156             this.transforms = Collections.EMPTY_LIST;
 157         } else {
 158             this.transforms = Collections.unmodifiableList(transforms);
 159         }
 160     }
 161 
 162     public String getURI() {
 163         return uri;
 164     }
 165 
 166     public String getType() {
 167         return type;
 168     }
 169 
 170     public List getTransforms() {
 171         return transforms;
 172     }
 173 
 174     public void marshal(Node parent, String dsPrefix, DOMCryptoContext context)
 175         throws MarshalException {
 176         Document ownerDoc = DOMUtils.getOwnerDocument(parent);
 177 
 178         Element rmElem = DOMUtils.createElement
 179             (ownerDoc, "RetrievalMethod", XMLSignature.XMLNS, dsPrefix);
 180 
 181         // add URI and Type attributes
 182         DOMUtils.setAttribute(rmElem, "URI", uri);
 183         DOMUtils.setAttribute(rmElem, "Type", type);
 184 
 185         // add Transforms elements
 186         if (!transforms.isEmpty()) {
 187             Element transformsElem = DOMUtils.createElement
 188                 (ownerDoc, "Transforms", XMLSignature.XMLNS, dsPrefix);
 189             rmElem.appendChild(transformsElem);
 190             for (int i = 0, size = transforms.size(); i < size; i++) {
 191                 ((DOMTransform) transforms.get(i)).marshal
 192                     (transformsElem, dsPrefix, context);
 193             }
 194         }
 195 
 196         parent.appendChild(rmElem);
 197 
 198         // save here node
 199         here = rmElem.getAttributeNodeNS(null, "URI");
 200     }
 201 
 202     public Node getHere() {
 203         return here;
 204     }
 205 
 206     public Data dereference(XMLCryptoContext context)
 207         throws URIReferenceException {
 208 
 209         if (context == null) {
 210             throw new NullPointerException("context cannot be null");
 211         }
 212 
 213         /*
 214          * If URIDereferencer is specified in context; use it, otherwise use
 215          * built-in.
 216          */
 217         URIDereferencer deref = context.getURIDereferencer();
 218         if (deref == null) {
 219             deref = DOMURIDereferencer.INSTANCE;
 220         }
 221 
 222         Data data = deref.dereference(this, context);
 223 
 224         // pass dereferenced data through Transforms
 225         try {
 226             for (int i = 0, size = transforms.size(); i < size; i++) {
 227                 Transform transform = (Transform) transforms.get(i);
 228                 data = ((DOMTransform) transform).transform(data, context);
 229             }
 230         } catch (Exception e) {
 231             throw new URIReferenceException(e);
 232         }
 233 
 234         // guard against RetrievalMethod loops
 235         if ((data instanceof NodeSetData) && Utils.secureValidation(context)) {
 236             NodeSetData nsd = (NodeSetData)data;
 237             Iterator i = nsd.iterator();
 238             if (i.hasNext()) {
 239                 Node root = (Node)i.next();
 240                 if ("RetrievalMethod".equals(root.getLocalName())) {
 241                     throw new URIReferenceException(
 242                         "It is forbidden to have one RetrievalMethod point " +
 243                         "to another when secure validation is enabled");
 244                 }
 245             }
 246         }
 247 
 248         return data;
 249     }
 250 
 251     public XMLStructure dereferenceAsXMLStructure(XMLCryptoContext context)
 252         throws URIReferenceException {
 253 
 254         try {
 255             ApacheData data = (ApacheData) dereference(context);
 256             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 257             dbf.setNamespaceAware(true);
 258             dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,
 259                            Boolean.TRUE);
 260             DocumentBuilder db = dbf.newDocumentBuilder();
 261             Document doc = db.parse(new ByteArrayInputStream
 262                 (data.getXMLSignatureInput().getBytes()));
 263             Element kiElem = doc.getDocumentElement();
 264             if (kiElem.getLocalName().equals("X509Data")) {
 265                 return new DOMX509Data(kiElem);
 266             } else {
 267                 return null; // unsupported
 268             }
 269         } catch (Exception e) {
 270             throw new URIReferenceException(e);
 271         }
 272     }
 273 
 274     public boolean equals(Object obj) {
 275         if (this == obj) {
 276             return true;
 277         }
 278         if (!(obj instanceof RetrievalMethod)) {
 279             return false;
 280         }
 281         RetrievalMethod orm = (RetrievalMethod) obj;
 282 
 283         boolean typesEqual = (type == null ? orm.getType() == null :
 284             type.equals(orm.getType()));
 285 
 286         return (uri.equals(orm.getURI()) &&
 287             transforms.equals(orm.getTransforms()) && typesEqual);
 288     }
 289 }