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 }