[Dive4elements-commits] [PATCH 09 of 21] Add SAML ticket validator classes

Wald Commits scm-commit at wald.intevation.org
Wed May 8 18:08:19 CEST 2013


# HG changeset patch
# User Bernhard Herzog <bh at intevation.de>
# Date 1368028573 -7200
# Node ID c1806821860b4bd5e9ebb8b84ad8dfbbb2b88509
# Parent  05da3cfa4054439c68e46af45611c86f8fa0f8e0
Add SAML ticket validator classes.

diff -r 05da3cfa4054 -r c1806821860b gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/TicketValidator.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/TicketValidator.java	Wed May 08 17:56:13 2013 +0200
@@ -0,0 +1,148 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.server.auth.saml;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.Key;
+import java.util.Iterator;
+import java.util.Date;
+import javax.security.cert.X509Certificate;
+import javax.security.cert.CertificateException;
+import javax.xml.crypto.Data;
+import javax.xml.crypto.NodeSetData;
+import javax.xml.crypto.dsig.Reference;
+import javax.xml.crypto.dsig.XMLSignature;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+import javax.xml.crypto.dsig.dom.DOMValidateContext;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Validator for SAML tickets.
+ */
+public class TicketValidator {
+
+    /**
+     * The logger used by the TicketValidator instances.
+     */
+    private static Logger logger = Logger.getLogger(TicketValidator.class);
+
+    /**
+     * The trusted Key for signature checks.
+     */
+    private Key trustedKey;
+
+    /**
+     * Creates a new TicketValidator from a trusted key.
+     * @param trustedKey  The trusted key for the signature checks.
+     */
+    public TicketValidator(Key trustedKey) {
+        this.trustedKey = trustedKey;
+    }
+
+    /**
+     * Creates a new TicketValidator, loading the trusted key from a
+     * file.
+     * @param filename The filename of the X509 certificate containing
+     * the trusted public key.
+     */
+    public TicketValidator(String filename) throws IOException,
+                                                   CertificateException {
+        this.trustedKey = loadKey(filename);
+    }
+
+    /**
+     * Loads the public key from a file containing an X509 certificate.
+     */
+    private Key loadKey(String filename) throws IOException,
+                                                CertificateException {
+        X509Certificate cert = X509Certificate.getInstance(
+                                               new FileInputStream(filename));
+        cert.checkValidity(new Date());
+        return cert.getPublicKey();
+    }
+
+
+    /**
+     * Check the ticket represented by the given DOM element.
+     * @param root the DOM element under which the signature can be
+     * found.
+     * @return The assertion element from the signed data.
+     */
+    public Assertion checkTicket(Element root) throws Exception {
+        markAssertionIdAttributes(root);
+
+        Node signode = XPathUtils.xpathNode(root, ".//ds:Signature");
+
+        DOMValidateContext context = new DOMValidateContext(this.trustedKey,
+                                                            signode);
+        context.setProperty("javax.xml.crypto.dsig.cacheReference", true);
+
+        XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
+        XMLSignature signature = factory.unmarshalXMLSignature(context);
+        if (!signature.validate(context)) {
+            logger.error("Signature of SAML ticket could not be validated.");
+            return null;
+        }
+
+        Element assertionElement = extractAssertion(signature, context);
+        if (assertionElement == null) {
+            logger.error("Could not extract assertion from signed content.");
+            return null;
+        }
+
+        Assertion assertion = new Assertion(assertionElement);
+        if (!assertion.isValidNow()) {
+            logger.error("Ticket is not valid now"
+                         + " (NotBefore: " + assertion.getFrom()
+                         + ", NotOnOrAfter: " + assertion.getUntil());
+            return null;
+        }
+
+        return assertion;
+    }
+
+    /**
+     * Mark the AssertionID attribute of SAML Assertion elements as ID
+     * attribute, so that the signature checker can resolve the
+     * references properly and find the signed data.
+     */
+    private void markAssertionIdAttributes(Element root) {
+        NodeList nodes = XPathUtils.xpathNodeList(root, "saml:Assertion");
+        for (int i = 0; i < nodes.getLength(); i++) {
+            Element el = (Element)nodes.item(i);
+            el.setIdAttribute("AssertionID", true);
+        }
+    }
+
+    private Element extractAssertion(XMLSignature sig,
+                                     DOMValidateContext context) {
+        for (Object obj: sig.getSignedInfo().getReferences()) {
+            Data data = ((Reference)obj).getDereferencedData();
+            if (data instanceof NodeSetData) {
+                Iterator i = ((NodeSetData)data).iterator();
+                for (int k = 0; i.hasNext(); k++) {
+                    Object node = i.next();
+                    if (node instanceof Element) {
+                        Element el = (Element)node;
+                        if (el.getTagName().equals("Assertion"))
+                            return el;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+}


More information about the Dive4elements-commits mailing list