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 package com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations; 24 25 import java.io.ByteArrayInputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.security.PublicKey; 29 import java.security.cert.CertificateException; 30 import java.security.cert.CertificateFactory; 31 import java.security.cert.X509Certificate; 32 import java.util.ArrayList; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.ListIterator; 36 import java.util.Set; 37 38 import javax.xml.parsers.ParserConfigurationException; 39 40 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; 41 import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; 42 import com.sun.org.apache.xml.internal.security.keys.content.RetrievalMethod; 43 import com.sun.org.apache.xml.internal.security.keys.content.x509.XMLX509Certificate; 44 import com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolver; 45 import com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolverException; 46 import com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolverSpi; 47 import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolver; 48 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; 49 import com.sun.org.apache.xml.internal.security.transforms.Transforms; 50 import com.sun.org.apache.xml.internal.security.utils.Constants; 51 import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 52 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver; 53 import org.w3c.dom.Attr; 54 import org.w3c.dom.Element; 55 import org.w3c.dom.Node; 56 import org.xml.sax.SAXException; 57 58 /** 59 * The RetrievalMethodResolver can retrieve public keys and certificates from 60 * other locations. The location is specified using the ds:RetrievalMethod 61 * element which points to the location. This includes the handling of raw 62 * (binary) X.509 certificate which are not encapsulated in an XML structure. 63 * If the retrieval process encounters an element which the 64 * RetrievalMethodResolver cannot handle itself, resolving of the extracted 65 * element is delegated back to the KeyResolver mechanism. 66 * 67 */ 68 public class RetrievalMethodResolver extends KeyResolverSpi { 69 70 private static final com.sun.org.slf4j.internal.Logger LOG = 71 com.sun.org.slf4j.internal.LoggerFactory.getLogger(RetrievalMethodResolver.class); 72 73 /** 74 * Method engineResolvePublicKey 75 * {@inheritDoc} 76 * @param element 77 * @param baseURI 78 * @param storage 79 */ engineLookupAndResolvePublicKey( Element element, String baseURI, StorageResolver storage )80 public PublicKey engineLookupAndResolvePublicKey( 81 Element element, String baseURI, StorageResolver storage 82 ) { 83 if (!XMLUtils.elementIsInSignatureSpace(element, Constants._TAG_RETRIEVALMETHOD)) { 84 return null; 85 } 86 87 try { 88 // Create a retrieval method over the given element 89 RetrievalMethod rm = new RetrievalMethod(element, baseURI); 90 String type = rm.getType(); 91 XMLSignatureInput resource = resolveInput(rm, baseURI, secureValidation); 92 if (RetrievalMethod.TYPE_RAWX509.equals(type)) { 93 // a raw certificate, direct parsing is done! 94 X509Certificate cert = getRawCertificate(resource); 95 if (cert != null) { 96 return cert.getPublicKey(); 97 } 98 return null; 99 } 100 Element e = obtainReferenceElement(resource, secureValidation); 101 102 // Check to make sure that the reference is not to another RetrievalMethod 103 // which points to this element 104 if (XMLUtils.elementIsInSignatureSpace(e, Constants._TAG_RETRIEVALMETHOD)) { 105 if (secureValidation) { 106 if (LOG.isDebugEnabled()) { 107 String error = "Error: It is forbidden to have one RetrievalMethod " 108 + "point to another with secure validation"; 109 LOG.debug(error); 110 } 111 return null; 112 } 113 RetrievalMethod rm2 = new RetrievalMethod(e, baseURI); 114 XMLSignatureInput resource2 = resolveInput(rm2, baseURI, secureValidation); 115 Element e2 = obtainReferenceElement(resource2, secureValidation); 116 if (e2 == element) { 117 LOG.debug("Error: Can't have RetrievalMethods pointing to each other"); 118 return null; 119 } 120 } 121 122 return resolveKey(e, baseURI, storage); 123 } catch (XMLSecurityException ex) { 124 LOG.debug("XMLSecurityException", ex); 125 } catch (CertificateException ex) { 126 LOG.debug("CertificateException", ex); 127 } catch (IOException ex) { 128 LOG.debug("IOException", ex); 129 } catch (ParserConfigurationException e) { 130 LOG.debug("ParserConfigurationException", e); 131 } catch (SAXException e) { 132 LOG.debug("SAXException", e); 133 } 134 return null; 135 } 136 137 /** 138 * Method engineResolveX509Certificate 139 * {@inheritDoc} 140 * @param element 141 * @param baseURI 142 * @param storage 143 */ engineLookupResolveX509Certificate( Element element, String baseURI, StorageResolver storage)144 public X509Certificate engineLookupResolveX509Certificate( 145 Element element, String baseURI, StorageResolver storage) { 146 if (!XMLUtils.elementIsInSignatureSpace(element, Constants._TAG_RETRIEVALMETHOD)) { 147 return null; 148 } 149 150 try { 151 RetrievalMethod rm = new RetrievalMethod(element, baseURI); 152 String type = rm.getType(); 153 XMLSignatureInput resource = resolveInput(rm, baseURI, secureValidation); 154 if (RetrievalMethod.TYPE_RAWX509.equals(type)) { 155 return getRawCertificate(resource); 156 } 157 158 Element e = obtainReferenceElement(resource, secureValidation); 159 160 // Check to make sure that the reference is not to another RetrievalMethod 161 // which points to this element 162 if (XMLUtils.elementIsInSignatureSpace(e, Constants._TAG_RETRIEVALMETHOD)) { 163 if (secureValidation) { 164 if (LOG.isDebugEnabled()) { 165 String error = "Error: It is forbidden to have one RetrievalMethod " 166 + "point to another with secure validation"; 167 LOG.debug(error); 168 } 169 return null; 170 } 171 RetrievalMethod rm2 = new RetrievalMethod(e, baseURI); 172 XMLSignatureInput resource2 = resolveInput(rm2, baseURI, secureValidation); 173 Element e2 = obtainReferenceElement(resource2, secureValidation); 174 if (e2 == element) { 175 LOG.debug("Error: Can't have RetrievalMethods pointing to each other"); 176 return null; 177 } 178 } 179 180 return resolveCertificate(e, baseURI, storage); 181 } catch (XMLSecurityException ex) { 182 LOG.debug("XMLSecurityException", ex); 183 } catch (CertificateException ex) { 184 LOG.debug("CertificateException", ex); 185 } catch (IOException ex) { 186 LOG.debug("IOException", ex); 187 } catch (ParserConfigurationException e) { 188 LOG.debug("ParserConfigurationException", e); 189 } catch (SAXException e) { 190 LOG.debug("SAXException", e); 191 } 192 return null; 193 } 194 195 /** 196 * Retrieves a x509Certificate from the given information 197 * @param e 198 * @param baseURI 199 * @param storage 200 * @return a x509Certificate from the given information 201 * @throws KeyResolverException 202 */ resolveCertificate( Element e, String baseURI, StorageResolver storage )203 private static X509Certificate resolveCertificate( 204 Element e, String baseURI, StorageResolver storage 205 ) throws KeyResolverException { 206 if (LOG.isDebugEnabled()) { 207 LOG.debug("Now we have a {" + e.getNamespaceURI() + "}" 208 + e.getLocalName() + " Element"); 209 } 210 // An element has been provided 211 if (e != null) { 212 return KeyResolver.getX509Certificate(e, baseURI, storage); 213 } 214 return null; 215 } 216 217 /** 218 * Retrieves a PublicKey from the given information 219 * @param e 220 * @param baseURI 221 * @param storage 222 * @return a PublicKey from the given information 223 * @throws KeyResolverException 224 */ resolveKey( Element e, String baseURI, StorageResolver storage )225 private static PublicKey resolveKey( 226 Element e, String baseURI, StorageResolver storage 227 ) throws KeyResolverException { 228 if (LOG.isDebugEnabled()) { 229 LOG.debug("Now we have a {" + e.getNamespaceURI() + "}" 230 + e.getLocalName() + " Element"); 231 } 232 // An element has been provided 233 if (e != null) { 234 return KeyResolver.getPublicKey(e, baseURI, storage); 235 } 236 return null; 237 } 238 obtainReferenceElement(XMLSignatureInput resource, boolean secureValidation)239 private static Element obtainReferenceElement(XMLSignatureInput resource, boolean secureValidation) 240 throws CanonicalizationException, ParserConfigurationException, 241 IOException, SAXException, KeyResolverException { 242 Element e; 243 if (resource.isElement()){ 244 e = (Element) resource.getSubNode(); 245 } else if (resource.isNodeSet()) { 246 // Retrieved resource is a nodeSet 247 e = getDocumentElement(resource.getNodeSet()); 248 } else { 249 // Retrieved resource is an inputStream 250 byte inputBytes[] = resource.getBytes(); 251 e = getDocFromBytes(inputBytes, secureValidation); 252 // otherwise, we parse the resource, create an Element and delegate 253 LOG.debug("we have to parse {} bytes", inputBytes.length); 254 } 255 return e; 256 } 257 getRawCertificate(XMLSignatureInput resource)258 private static X509Certificate getRawCertificate(XMLSignatureInput resource) 259 throws CanonicalizationException, IOException, CertificateException { 260 byte inputBytes[] = resource.getBytes(); 261 // if the resource stores a raw certificate, we have to handle it 262 CertificateFactory certFact = 263 CertificateFactory.getInstance(XMLX509Certificate.JCA_CERT_ID); 264 try (InputStream is = new ByteArrayInputStream(inputBytes)) { 265 return (X509Certificate) certFact.generateCertificate(is); 266 } 267 } 268 269 /** 270 * Resolves the input from the given retrieval method 271 * @return the input from the given retrieval method 272 * @throws XMLSecurityException 273 */ resolveInput( RetrievalMethod rm, String baseURI, boolean secureValidation )274 private static XMLSignatureInput resolveInput( 275 RetrievalMethod rm, String baseURI, boolean secureValidation 276 ) throws XMLSecurityException { 277 Attr uri = rm.getURIAttr(); 278 // Apply the transforms 279 Transforms transforms = rm.getTransforms(); 280 ResourceResolver resRes = ResourceResolver.getInstance(uri, baseURI, secureValidation); 281 XMLSignatureInput resource = resRes.resolve(uri, baseURI, secureValidation); 282 if (transforms != null) { 283 LOG.debug("We have Transforms"); 284 resource = transforms.performTransforms(resource); 285 } 286 return resource; 287 } 288 289 /** 290 * Method engineResolveSecretKey 291 * {@inheritDoc} 292 * @param element 293 * @param baseURI 294 * @param storage 295 */ engineLookupAndResolveSecretKey( Element element, String baseURI, StorageResolver storage )296 public javax.crypto.SecretKey engineLookupAndResolveSecretKey( 297 Element element, String baseURI, StorageResolver storage 298 ) { 299 return null; 300 } 301 getDocumentElement(Set<Node> set)302 private static Element getDocumentElement(Set<Node> set) { 303 Iterator<Node> it = set.iterator(); 304 Element e = null; 305 while (it.hasNext()) { 306 Node currentNode = it.next(); 307 if (currentNode != null && Node.ELEMENT_NODE == currentNode.getNodeType()) { 308 e = (Element) currentNode; 309 break; 310 } 311 } 312 List<Node> parents = new ArrayList<>(); 313 314 // Obtain all the parents of the elemnt 315 while (e != null) { 316 parents.add(e); 317 Node n = e.getParentNode(); 318 if (n == null || Node.ELEMENT_NODE != n.getNodeType()) { 319 break; 320 } 321 e = (Element) n; 322 } 323 // Visit them in reverse order. 324 ListIterator<Node> it2 = parents.listIterator(parents.size()-1); 325 Element ele = null; 326 while (it2.hasPrevious()) { 327 ele = (Element) it2.previous(); 328 if (set.contains(ele)) { 329 return ele; 330 } 331 } 332 return null; 333 } 334 } 335