All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dk.itst.oiosaml.sp.service.util.Utils Maven / Gradle / Ivy

Go to download

SAML Servlet Filter, configured to work with the danish SAML profile OIOSAML 2.0.9

The newest version!
/*
 * The contents of this file are subject to the Mozilla Public 
 * License Version 1.1 (the "License"); you may not use this 
 * file except in compliance with the License. You may obtain 
 * a copy of the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an 
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express 
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 *
 * The Original Code is OIOSAML Java Service Provider.
 * 
 * The Initial Developer of the Original Code is Trifork A/S. Portions 
 * created by Trifork A/S are Copyright (C) 2008 Danish National IT 
 * and Telecom Agency (http://www.itst.dk). All Rights Reserved.
 * 
 * Contributor(s):
 *   Joakim Recht 
 *   Rolf Njor Jensen 
 *
 */
package dk.itst.oiosaml.sp.service.util;

import java.lang.reflect.Constructor;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletContext;

import dk.itst.oiosaml.logging.Logger;
import dk.itst.oiosaml.logging.LoggerFactory;
import org.apache.commons.configuration.Configuration;
import org.opensaml.ws.soap.util.SOAPConstants;
import org.opensaml.xml.util.Base64;

import dk.itst.oiosaml.common.OIOSAMLConstants;
import dk.itst.oiosaml.error.Layer;
import dk.itst.oiosaml.error.WrappedException;
import dk.itst.oiosaml.sp.service.SAMLHandler;

/**
 * Utility class used for signing SAML documents and verifying the signed
 * documents received from the Login Site
 * 
 */
public final class Utils {

	public static final String VERSION = "$Id: Utils.java 3197 2008-07-25 07:47:33Z jre $";
	private static final Logger log = LoggerFactory.getLogger(Utils.class);
	private static final String[] SOAP_VERSIONS = new String[] { SOAPConstants.SOAP11_NS, SOAPConstants.SOAP12_NS};


	/**
	 * Making nice XML for output in browser, i.e. converting < to &lt;, > to
	 * &gt; etc.
	 */
	public static String makeXML(String param) {
		String xml = param;
		if (xml != null && !"".equals(xml)) {
			xml = xml.replaceAll("><", ">\n<");
			xml = xml.replaceAll("<", "<");
			xml = xml.replaceAll(">", ">");
			xml = xml.replaceAll("\n", "
"); } return xml; } /** * Encode a string to html entities. */ public static String htmlEntityEncode(String s) { StringBuilder buf = new StringBuilder(); int len = (s == null ? -1 : s.length()); for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') { buf.append(c); } else { buf.append("&#" + (int) c + ";"); } } return buf.toString(); } /** * @return true if the queryString in the request has been signed correctly * by the Login Site */ public static boolean verifySignature(String signature, String queryString, String queryParameter, PublicKey publicKey) { // Verifying the signature.... if (log.isDebugEnabled()) log.debug("signature..:" + signature); if (signature == null) { return false; } byte[] buffer = Base64.decode(signature); String data = parseSignedQueryString(queryString, queryParameter); // String data = queryString.substring(queryString.indexOf(firstQueryParameter), queryString.lastIndexOf("&")); if (log.isDebugEnabled()) log.debug("data.......:" + data); if (log.isDebugEnabled()) log.debug("Verifying Signature..."); return verifySignature(data.getBytes(), publicKey, buffer); } /** * Check if a SAML HTTP Redirect has been signed by the expected certificate * * @param data * The query parameters in the HTTP Redirect, which has been * signed * @param key * The public key of the certificate from the expected sender * @param sig * The signature generated by the sender after it has been base64 * decoded * @return true, if the signature is valid, otherwise false */ public static boolean verifySignature(byte[] data, PublicKey key, byte[] sig) { if (log.isDebugEnabled()) { log.debug("data...:" + new String(data)); log.debug("sig....:" + new String(sig)); log.debug("key....:" + key.toString()); } try { boolean valid = false; try { Signature signer = Signature.getInstance(OIOSAMLConstants.SHA256_WITH_RSA); signer.initVerify(key); signer.update(data); valid = signer.verify(sig); } catch (Exception ex) { // the above code may throw an exception due to incorrect digest size, // or similar - we will catch them all, and try with SHA-1 instead ; } // if SHA-256 did not validate, we try with SHA-1 if (!valid) { Signature signer = Signature.getInstance(OIOSAMLConstants.SHA1_WITH_RSA); signer.initVerify(key); signer.update(data); valid = signer.verify(sig); } return valid; } catch (InvalidKeyException e) { throw new WrappedException(Layer.CLIENT, e); } catch (NoSuchAlgorithmException e) { throw new WrappedException(Layer.CLIENT, e); } catch (SignatureException e) { log.error("Failed to verify signature", e); return false; } } /** * @return A beautified xml string */ public static String beautifyAndHtmlXML(String xml, String split) { return makeXML(beautifyXML(xml, split)); } /** * @return A beautified xml string */ public static String beautifyXML(String xml, String split) { String s = ""; if (split != null) s = ".:split:."; if (xml == null || "".equals(xml)) return xml; StringBuffer result = new StringBuffer(); String[] results = xml.split("<"); for (int i = 1; i < results.length; i++) { results[i] = "<" + results[i].trim(); if (results[i].endsWith("/>")) { result.append(results[i]).append(s); } else if (results[i].startsWith("")) { result.append(results[i]).append(s); } else { result.append(results[i]); } } // result = result.trim(); if (split == null) return result.toString().trim(); StringBuilder newResult = new StringBuilder(); String ident = ""; results = result.toString().split(s); for (int i = 0; i < results.length; i++) { if (results[i].startsWith("") == -1) ident += split; } return newResult.toString(); } /** * Generate a valid xs:ID string. */ public static String generateUUID() { return "_" + UUID.randomUUID().toString(); } /** * Get the SOAP version from an Envelope. * @param xml The complete envelope as a String. * @return The SOAP version, represented by the SOAP namespace. Returns null if no namespace was found. */ public static String getSoapVersion(String xml) { for (int i = 0; i < SOAP_VERSIONS.length; i++) { int idx = xml.indexOf(SOAP_VERSIONS[i]); if (idx > -1) { String prefix = getPrefix(xml, idx); int start = xml.lastIndexOf('<', idx); if (prefix == null) { prefix = "<"; } else { prefix = "<" + prefix + ":"; } if (xml.lastIndexOf(prefix + "Envelope", idx) >= start) { return SOAP_VERSIONS[i]; } } } return null; } private static String getPrefix(String xml, int idx) { if (idx > -1) { String prefix = xml.substring(xml.lastIndexOf(' ', idx) + 1, idx); if (prefix.startsWith("xmlns:")) { prefix = prefix.substring(6, prefix.lastIndexOf('=')).trim(); } else { prefix = null; } return prefix; } return null; } public static Object newInstance(Configuration cfg, String property) { String name = cfg.getString(property); if (name == null) { throw new IllegalArgumentException("Property " + property + " has not been set"); } try { Class c = Class.forName(name); return c.newInstance(); } catch (Exception e) { throw new RuntimeException("Unable to create instance of " + name, e); } } public static Map getHandlers(Configuration config, ServletContext servletContext) { Map handlers = new HashMap(); for (Iterator i = config.getKeys(); i.hasNext();) { String key = (String) i.next(); if (!key.startsWith("oiosaml-sp.protocol.endpoints.")) continue; log.debug("Checking " + key); try { Class c = Class.forName(config.getString(key)); SAMLHandler instance; try { Constructor constructor = c.getConstructor(Configuration.class); instance = (SAMLHandler) constructor.newInstance(config); } catch (NoSuchMethodException e) { try { Constructor constructor = c.getConstructor(ServletContext.class); instance = (SAMLHandler) constructor.newInstance(servletContext); } catch (NoSuchMethodException ex) { instance = (SAMLHandler) c.newInstance(); } } handlers.put(key.substring(key.lastIndexOf('.') + 1), instance); } catch (Exception e) { log.error("Unable to instantiate " + key + ": " + config.getString(key), e); throw new RuntimeException(e); } } return handlers; } public static String parseSignedQueryString(String queryString, String queryParameter) { StringBuilder s = new StringBuilder(); String samlRequestOrResponse = Utils.getParameter(queryParameter, queryString); String relayState = Utils.getParameter(Constants.SAML_RELAYSTATE, queryString); String sigAlg = Utils.getParameter(Constants.SAML_SIGALG, queryString); // see order in http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf line 601->605 s.append(queryParameter); s.append("="); s.append(samlRequestOrResponse); if(relayState != null) { s.append("&"); s.append(Constants.SAML_RELAYSTATE); s.append("="); s.append(relayState); } s.append("&"); s.append(Constants.SAML_SIGALG); s.append("="); s.append(sigAlg); return s.toString(); } public static String getParameter(String name, String url) { int qpos = url.indexOf('?') + 1; String[] parts = url.substring(qpos).split("&"); for (String part : parts) { int pos = part.indexOf('='); String key = part.substring(0, pos); if (name.equals(key)) { return part.substring(pos + 1); } } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy