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