Mercurial > dive4elements > river
comparison gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/TicketValidator.java @ 5941:c1806821860b
Add SAML ticket validator classes.
author | Bernhard Herzog <bh@intevation.de> |
---|---|
date | Wed, 08 May 2013 17:56:13 +0200 |
parents | |
children | 0a0b4bfdf372 |
comparison
equal
deleted
inserted
replaced
5940:05da3cfa4054 | 5941:c1806821860b |
---|---|
1 /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde | |
2 * Software engineering by Intevation GmbH | |
3 * | |
4 * This file is Free Software under the GNU AGPL (>=v3) | |
5 * and comes with ABSOLUTELY NO WARRANTY! Check out the | |
6 * documentation coming with Dive4Elements River for details. | |
7 */ | |
8 | |
9 package org.dive4elements.river.client.server.auth.saml; | |
10 | |
11 import java.io.FileInputStream; | |
12 import java.io.IOException; | |
13 import java.security.Key; | |
14 import java.util.Iterator; | |
15 import java.util.Date; | |
16 import javax.security.cert.X509Certificate; | |
17 import javax.security.cert.CertificateException; | |
18 import javax.xml.crypto.Data; | |
19 import javax.xml.crypto.NodeSetData; | |
20 import javax.xml.crypto.dsig.Reference; | |
21 import javax.xml.crypto.dsig.XMLSignature; | |
22 import javax.xml.crypto.dsig.XMLSignatureFactory; | |
23 import javax.xml.crypto.dsig.dom.DOMValidateContext; | |
24 | |
25 import org.apache.log4j.Logger; | |
26 | |
27 import org.w3c.dom.Element; | |
28 import org.w3c.dom.Node; | |
29 import org.w3c.dom.NodeList; | |
30 | |
31 /** | |
32 * Validator for SAML tickets. | |
33 */ | |
34 public class TicketValidator { | |
35 | |
36 /** | |
37 * The logger used by the TicketValidator instances. | |
38 */ | |
39 private static Logger logger = Logger.getLogger(TicketValidator.class); | |
40 | |
41 /** | |
42 * The trusted Key for signature checks. | |
43 */ | |
44 private Key trustedKey; | |
45 | |
46 /** | |
47 * Creates a new TicketValidator from a trusted key. | |
48 * @param trustedKey The trusted key for the signature checks. | |
49 */ | |
50 public TicketValidator(Key trustedKey) { | |
51 this.trustedKey = trustedKey; | |
52 } | |
53 | |
54 /** | |
55 * Creates a new TicketValidator, loading the trusted key from a | |
56 * file. | |
57 * @param filename The filename of the X509 certificate containing | |
58 * the trusted public key. | |
59 */ | |
60 public TicketValidator(String filename) throws IOException, | |
61 CertificateException { | |
62 this.trustedKey = loadKey(filename); | |
63 } | |
64 | |
65 /** | |
66 * Loads the public key from a file containing an X509 certificate. | |
67 */ | |
68 private Key loadKey(String filename) throws IOException, | |
69 CertificateException { | |
70 X509Certificate cert = X509Certificate.getInstance( | |
71 new FileInputStream(filename)); | |
72 cert.checkValidity(new Date()); | |
73 return cert.getPublicKey(); | |
74 } | |
75 | |
76 | |
77 /** | |
78 * Check the ticket represented by the given DOM element. | |
79 * @param root the DOM element under which the signature can be | |
80 * found. | |
81 * @return The assertion element from the signed data. | |
82 */ | |
83 public Assertion checkTicket(Element root) throws Exception { | |
84 markAssertionIdAttributes(root); | |
85 | |
86 Node signode = XPathUtils.xpathNode(root, ".//ds:Signature"); | |
87 | |
88 DOMValidateContext context = new DOMValidateContext(this.trustedKey, | |
89 signode); | |
90 context.setProperty("javax.xml.crypto.dsig.cacheReference", true); | |
91 | |
92 XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM"); | |
93 XMLSignature signature = factory.unmarshalXMLSignature(context); | |
94 if (!signature.validate(context)) { | |
95 logger.error("Signature of SAML ticket could not be validated."); | |
96 return null; | |
97 } | |
98 | |
99 Element assertionElement = extractAssertion(signature, context); | |
100 if (assertionElement == null) { | |
101 logger.error("Could not extract assertion from signed content."); | |
102 return null; | |
103 } | |
104 | |
105 Assertion assertion = new Assertion(assertionElement); | |
106 if (!assertion.isValidNow()) { | |
107 logger.error("Ticket is not valid now" | |
108 + " (NotBefore: " + assertion.getFrom() | |
109 + ", NotOnOrAfter: " + assertion.getUntil()); | |
110 return null; | |
111 } | |
112 | |
113 return assertion; | |
114 } | |
115 | |
116 /** | |
117 * Mark the AssertionID attribute of SAML Assertion elements as ID | |
118 * attribute, so that the signature checker can resolve the | |
119 * references properly and find the signed data. | |
120 */ | |
121 private void markAssertionIdAttributes(Element root) { | |
122 NodeList nodes = XPathUtils.xpathNodeList(root, "saml:Assertion"); | |
123 for (int i = 0; i < nodes.getLength(); i++) { | |
124 Element el = (Element)nodes.item(i); | |
125 el.setIdAttribute("AssertionID", true); | |
126 } | |
127 } | |
128 | |
129 private Element extractAssertion(XMLSignature sig, | |
130 DOMValidateContext context) { | |
131 for (Object obj: sig.getSignedInfo().getReferences()) { | |
132 Data data = ((Reference)obj).getDereferencedData(); | |
133 if (data instanceof NodeSetData) { | |
134 Iterator i = ((NodeSetData)data).iterator(); | |
135 for (int k = 0; i.hasNext(); k++) { | |
136 Object node = i.next(); | |
137 if (node instanceof Element) { | |
138 Element el = (Element)node; | |
139 if (el.getTagName().equals("Assertion")) | |
140 return el; | |
141 } | |
142 } | |
143 } | |
144 } | |
145 | |
146 return null; | |
147 } | |
148 } |