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