2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 package edu.internet2.middleware.shibboleth.shire;
52 import java.io.IOException;
53 import java.security.Key;
54 import java.security.KeyStore;
55 import java.security.PublicKey;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.Vector;
60 import org.apache.log4j.Logger;
61 import org.apache.xml.security.c14n.Canonicalizer;
62 import org.apache.xml.security.exceptions.XMLSecurityException;
63 import org.apache.xml.security.keys.KeyInfo;
64 import org.apache.xml.security.signature.Reference;
65 import org.apache.xml.security.signature.SignedInfo;
66 import org.apache.xml.security.signature.XMLSignature;
67 import org.apache.xml.security.transforms.Transforms;
68 import org.w3c.dom.Document;
69 import org.w3c.dom.Element;
70 import org.w3c.dom.Node;
71 import org.w3c.dom.NodeList;
72 import org.xml.sax.SAXException;
74 import edu.internet2.middleware.shibboleth.common.OriginSiteMapper;
75 import edu.internet2.middleware.shibboleth.common.OriginSiteMapperException;
76 import edu.internet2.middleware.shibboleth.common.XML;
79 * OriginSiteMapper implementation using an XML file to populate an in-memory
80 * database from an optionally-signed XML file
82 * @author Scott Cantor
83 * @created June 8, 2002
85 public class XMLOriginSiteMapper implements OriginSiteMapper {
87 private HashMap originSites = null;
88 private HashMap hsKeys = null;
89 private KeyStore ks = null;
90 private static Logger log = Logger.getLogger(XMLOriginSiteMapper.class.getName());
93 * Constructor for the XMLOriginSiteMapper object
95 * @param registryURI Tells where to find/download origin
97 * @param verifyKey Optional key to verify signature with
98 * @param ks Key store containing the trusted roots
100 * @exception Exception Raised if the registry file cannot be
104 public XMLOriginSiteMapper(String registryURI, Key verifyKey, KeyStore ks)
105 throws OriginSiteMapperException {
107 originSites = new HashMap();
108 hsKeys = new HashMap();
111 Document doc=org.opensaml.XML.parserPool.parse(registryURI);
112 log.info("Located site file (" +registryURI +").");
113 Element e = doc.getDocumentElement();
114 if (!XML.SHIB_NS.equals(e.getNamespaceURI()) || !"Sites".equals(e.getLocalName())) {
115 log.error("Construction requires a valid site file: (shib:Sites as root element)");
116 throw new OriginSiteMapperException("Construction requires a valid site file: (shib:Sites as root element)");
119 // Loop over the OriginSite elements.
120 NodeList nlist = e.getElementsByTagNameNS(XML.SHIB_NS, "OriginSite");
121 for (int i = 0; nlist != null && i < nlist.getLength(); i++) {
122 String os_name = ((Element) nlist.item(i)).getAttributeNS(null, "Name").trim();
123 if (os_name.length() == 0)
126 OriginSite os_obj = new OriginSite(os_name);
127 originSites.put(os_name, os_obj);
129 Node os_child = nlist.item(i).getFirstChild();
130 while (os_child != null) {
131 if (os_child.getNodeType() != Node.ELEMENT_NODE) {
132 os_child = os_child.getNextSibling();
136 // Process the various kinds of OriginSite children that we care about...
137 if (XML.SHIB_NS.equals(os_child.getNamespaceURI())
138 && "HandleService".equals(os_child.getLocalName())) {
139 String hs_name = ((Element) os_child).getAttributeNS(null, "Name").trim();
140 if (hs_name.length() > 0) {
141 os_obj.handleServices.add(hs_name);
143 // Check for KeyInfo.
144 Node ki = os_child.getFirstChild();
145 while (ki != null && ki.getNodeType() != Node.ELEMENT_NODE)
146 ki = ki.getNextSibling();
148 && org.opensaml.XML.XMLSIG_NS.equals(ki.getNamespaceURI())
149 && "KeyInfo".equals(ki.getLocalName())) {
151 KeyInfo kinfo = new KeyInfo((Element) ki, null);
152 PublicKey pubkey = kinfo.getPublicKey();
154 hsKeys.put(hs_name, pubkey);
155 } catch (XMLSecurityException exc) {
160 XML.SHIB_NS.equals(os_child.getNamespaceURI())
161 && "Domain".equals(os_child.getLocalName())) {
162 String dom = os_child.getFirstChild().getNodeValue().trim();
163 if (dom.length() > 0)
164 os_obj.domains.add(dom);
166 os_child = os_child.getNextSibling();
170 if (verifyKey != null) {
171 log.info("Initialized with a key: attempting to verify document signature.");
172 validateSignature(verifyKey, e);
174 log.info("Initialized without key: skipping signature verification.");
177 } catch (SAXException e) {
178 log.error("Problem parsing site configuration" + e);
179 throw new OriginSiteMapperException("Problem parsing site configuration" + e);
180 } catch (IOException e) {
181 log.error("Problem accessing site configuration" + e);
182 throw new OriginSiteMapperException("Problem accessing site configuration" + e);
183 } catch (org.opensaml.SAMLException e) {
184 log.error("Problem configuring parser: " + e);
185 throw new OriginSiteMapperException("Problem configuring parser: " + e);
189 private void validateSignature(Key verifyKey, Element e) throws OriginSiteMapperException {
191 Node n = e.getLastChild();
192 while (n != null && n.getNodeType() != Node.ELEMENT_NODE)
193 n = n.getPreviousSibling();
196 && org.opensaml.XML.XMLSIG_NS.equals(n.getNamespaceURI())
197 && "Signature".equals(n.getLocalName())) {
198 log.info("Located signature in document... verifying.");
200 XMLSignature sig = new XMLSignature((Element) n, null);
201 if (sig.checkSignatureValue(verifyKey)) {
202 // Now we verify that what is signed is what we expect.
203 SignedInfo sinfo = sig.getSignedInfo();
204 if (sinfo.getLength() == 1
206 .getCanonicalizationMethodURI()
207 .equals(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS)
208 || sinfo.getCanonicalizationMethodURI().equals(
209 Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS))) {
210 Reference ref = sinfo.item(0);
211 if (ref.getURI() == null || ref.getURI().equals("")) {
212 Transforms trans = ref.getTransforms();
213 if (trans.getLength() == 1
214 && trans.item(0).getURI().equals(Transforms.TRANSFORM_ENVELOPED_SIGNATURE))
215 log.info("Signature verification successful.");
219 "Unable to verify signature on registry file: Unsupported dsig reference or transform data submitted with signature.");
220 throw new OriginSiteMapperException("Unable to verify signature on registry file: Unsupported dsig reference or transform data submitted with signature.");
223 "Unable to verify signature on registry file: Unsupported canonicalization method.");
224 throw new OriginSiteMapperException("Unable to verify signature on registry file: Unsupported canonicalization method.");
228 "Unable to verify signature on registry file: signature cannot be verified with the specified key.");
229 throw new OriginSiteMapperException("Unable to verify signature on registry file: signature cannot be verified with the specified key.");
231 } catch (Exception sigE) {
233 "Unable to verify signature on registry file: An error occured while attempting to verify the signature:"
235 throw new OriginSiteMapperException(
236 "Unable to verify signature on registry file: An error occured while attempting to verify the signature:"
240 log.error("Unable to verify signature on registry file: no signature found in document.");
241 throw new OriginSiteMapperException("Unable to verify signature on registry file: no signature found in document.");
247 * Provides an iterator over the trusted Handle Services for the specified
250 * @param originSite The DNS name of the origin site to query
251 * @return An iterator over the Handle Service DNS names
253 public Iterator getHandleServiceNames(String originSite) {
254 OriginSite o = (OriginSite) originSites.get(originSite);
256 return o.handleServices.iterator();
261 * Returns a preconfigured key to use in verifying a signature created by
262 * the specified HS<P>
264 * Any key returned is implicitly trusted and a certificate signed by
265 * another trusted entity is not sought or required
267 * @param handleService Description of Parameter
268 * @return A trusted key (probably public but could be
271 public Key getHandleServiceKey(String handleService) {
272 return (Key) hsKeys.get(handleService);
276 * Provides an iterator over the security domain expressions for which the
277 * specified origin site is considered to be authoritative
279 * @param originSite The DNS name of the origin site to query
280 * @return An iterator over a set of regular expression strings
282 public Iterator getSecurityDomains(String originSite) {
283 OriginSite o = (OriginSite) originSites.get(originSite);
285 return o.domains.iterator();
290 * Gets a key store containing certificate entries that are trusted to sign
291 * Handle Service certificates that are encountered during processing<P>
295 * @return A key store containing trusted certificate issuers
297 public KeyStore getTrustedRoots() {
301 private class OriginSite {
303 private Vector domains = null;
304 private Vector handleServices = null;
306 private OriginSite(String name) {
307 domains = new Vector();
309 handleServices = new Vector();