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, 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 int transformCount = 0; 140 if (transformsElem != null) { 141 Element transformElem = 142 DOMUtils.getFirstChildElement(transformsElem); 143 while (transformElem != null) { 144 transforms.add 145 (new DOMTransform(transformElem, context, provider)); 146 transformElem = DOMUtils.getNextSiblingElement(transformElem); 147 148 transformCount++; 149 if (secVal && (transformCount > DOMReference.MAXIMUM_TRANSFORM_COUNT)) { 150 String error = "A maxiumum of " + DOMReference.MAXIMUM_TRANSFORM_COUNT + " " 151 + "transforms per Reference are allowed with secure validation"; 152 throw new MarshalException(error); 153 } 154 } 155 } 156 if (transforms.isEmpty()) { 157 this.transforms = Collections.emptyList(); 158 } else { 159 this.transforms = Collections.unmodifiableList(transforms); 160 } 161 } 162 163 public String getURI() { 164 return uri; 165 } 166 167 public String getType() { 168 return type; 169 } 170 171 public List getTransforms() { 172 return transforms; 173 } 174 175 public void marshal(Node parent, String dsPrefix, DOMCryptoContext context) 176 throws MarshalException 177 { 178 Document ownerDoc = DOMUtils.getOwnerDocument(parent); 179 Element rmElem = DOMUtils.createElement(ownerDoc, "RetrievalMethod", 180 XMLSignature.XMLNS, dsPrefix); 181 182 // add URI and Type attributes 183 DOMUtils.setAttribute(rmElem, "URI", uri); 184 DOMUtils.setAttribute(rmElem, "Type", type); 185 186 // add Transforms elements 187 if (!transforms.isEmpty()) { 188 Element transformsElem = DOMUtils.createElement(ownerDoc, 189 "Transforms", 190 XMLSignature.XMLNS, 191 dsPrefix); 192 rmElem.appendChild(transformsElem); 193 for (Transform transform : transforms) { 194 ((DOMTransform)transform).marshal(transformsElem, 195 dsPrefix, context); 196 } 197 } 198 199 parent.appendChild(rmElem); 200 201 // save here node 202 here = rmElem.getAttributeNodeNS(null, "URI"); 203 } 204 205 public Node getHere() { 206 return here; 207 } 208 209 public Data dereference(XMLCryptoContext context) 210 throws URIReferenceException 211 { 212 if (context == null) { 213 throw new NullPointerException("context cannot be null"); 214 } 215 216 /* 217 * If URIDereferencer is specified in context; use it, otherwise use 218 * built-in. 219 */ 220 URIDereferencer deref = context.getURIDereferencer(); 221 if (deref == null) { 222 deref = DOMURIDereferencer.INSTANCE; 223 } 224 225 Data data = deref.dereference(this, context); 226 227 // pass dereferenced data through Transforms 228 try { 229 for (Transform transform : transforms) { 230 data = ((DOMTransform)transform).transform(data, context); 231 } 232 } catch (Exception e) { 233 throw new URIReferenceException(e); 234 } 235 236 // guard against RetrievalMethod loops 237 if ((data instanceof NodeSetData) && Utils.secureValidation(context)) { 238 NodeSetData nsd = (NodeSetData)data; 239 Iterator i = nsd.iterator(); 240 if (i.hasNext()) { 241 Node root = (Node)i.next(); 242 if ("RetrievalMethod".equals(root.getLocalName())) { 243 throw new URIReferenceException( 244 "It is forbidden to have one RetrievalMethod point " + 245 "to another when secure validation is enabled"); 246 } 247 } 248 } 249 250 return data; 251 } 252 253 public XMLStructure dereferenceAsXMLStructure(XMLCryptoContext context) 254 throws URIReferenceException 255 { 256 try { 257 ApacheData data = (ApacheData)dereference(context); 258 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 259 dbf.setNamespaceAware(true); 260 dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); 261 DocumentBuilder db = dbf.newDocumentBuilder(); 262 Document doc = db.parse(new ByteArrayInputStream 263 (data.getXMLSignatureInput().getBytes())); 264 Element kiElem = doc.getDocumentElement(); 265 if (kiElem.getLocalName().equals("X509Data")) { 266 return new DOMX509Data(kiElem); 267 } else { 268 return null; // unsupported 269 } 270 } catch (Exception e) { 271 throw new URIReferenceException(e); 272 } 273 } 274 275 @Override 276 public boolean equals(Object obj) { 277 if (this == obj) { 278 return true; 279 } 280 if (!(obj instanceof RetrievalMethod)) { 281 return false; 282 } 283 RetrievalMethod orm = (RetrievalMethod)obj; 284 285 boolean typesEqual = (type == null ? orm.getType() == null 286 : type.equals(orm.getType())); 287 288 return (uri.equals(orm.getURI()) && 289 transforms.equals(orm.getTransforms()) && typesEqual); 290 } 291 292 @Override 293 public int hashCode() { 294 int result = 17; 295 if (type != null) { 296 result = 31 * result + type.hashCode(); 297 } 298 result = 31 * result + uri.hashCode(); 299 result = 31 * result + transforms.hashCode(); 300 301 return result; 302 } 303 }