Page tree

The OpenSAML 2 software has reached its End of Life and is no longer supported. This documentation is available for historical purposes only.

Skip to end of metadata
Go to start of metadata

This was provided by Frederik Libert of SMALS.be in this thread: http://groups.google.com/group/opensaml-users/browse_thread/thread/6a79744c24436559/5a39c675d78be14c

It extends the RPC generic handler (javax.xml.rpc.handler.GenericHandler).
It checks the outgoing SOAPBody on clientside for a SAMLRequest, Response or Assertion.
If it finds one, the saml is parsed into opensaml API, it is signed and the SOAPBody child-element containing the SAML is replaced with a new child-element containing the signed SAML message.

It has no vendor dependent code so I think it can be used in different java-client-webservice-implementations.
You may need to do some tweeking though.
For example, I tested with Weblogic 10.3 and had to set the following system property:
System.setProperty("weblogic.wsee.handler.allowAllModification", "true");
Without this property, you are not allowed to modify the SOAPMessage the way I did.
Some other webservice implementations may not need this handler if they do not break the signature when building the soapmessage, but even then it might be useful as this handler makes sure that the signature is based on (and included in) the xml that will eventually be sent to the webservice.

Two final remarks:
- if there are other SOAPHandlers configured on the client after the SamlSignSoapHandler, they may no longer modify the SOAPBody-content otherwise the SAML signature will be broken.
- if the webservice is secured with WS-Policy or something else that requires the client to sign the entire SOAPBody or more, the SamlSignSoapHandler must be executed before this signature is set by the client, otherwise the WS-Policy-signature will be broken.

 

SamlSignSoapHandler.java
package org.eh.sts.wsclient.handler;import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.rpc.handler.GenericHandler;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.opensaml.SAMLAssertion;
import org.opensaml.SAMLRequest;
import org.opensaml.SAMLResponse;
import org.opensaml.SAMLSignedObject;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.eh.sts.wsclient.STSClientException;
import org.eh.sts.wsclient.support.SAMLSignFactory;

/**
 *
 SOAP Handler that signs SAML Messages contained in SOAPBody. 
<br/> Supports signing of SAML Requests, Responses and Assertions.
 <br/>
 * Webservice-call must be document-style with the SAML Element as input message.
 * 
 * @author Frederik Libert
 * 
 * @since 1.0.0
 * 
 */
public class SamlSignSoapHandler extends GenericHandler {
    private static final String CRYPTO_FILE = "cryptoFile";
    private QName[] headers;
    /** cryptographic properties to sign SAML Message. */
    Properties crypto;
    /**
     * @see javax.xml.rpc.handler.GenericHandler#init(javax.xml.rpc.handler.HandlerInfo)
     */
    @Override
    public void init(HandlerInfo config) {
        this.crypto = new Properties();
        String cryptoFile = System.getProperty("SamlSignSoapHandler.cryptoFile");
        if (cryptoFile == null) {
            cryptoFile = (String) config.getHandlerConfig().get(CRYPTO_FILE);
        }
        if (cryptoFile == null) {
 
           throw new STSClientException("Correct location of file with 
cryptographic info must be set through either a System-property or a 
handler-config parameter");
        }
        try {
            this.crypto.load(SamlSignSoapHandler.class.getResourceAsStream(cryptoFile));
        } catch (Exception e) {
            throw new STSClientException("Could not load cryptographic properties to sign SAML Message", e);
        }
    }
    /**
     * 
     * @see javax.xml.rpc.handler.GenericHandler#handleRequest(javax.xml.rpc.handler.MessageContext)
     */
    public boolean handleRequest(MessageContext context) {
        SOAPMessageContext messageContext = (SOAPMessageContext) context;
        try {
            System.out.println("SamlSignSoapHandler -- signing SAML in SOAPBody ...");
            Node nodeBeforeSigning = messageContext.getMessage().getSOAPBody().getFirstChild();
            DOMSource source = new DOMSource(nodeBeforeSigning);
            SAMLSignedObject request = transformToSAML(source);
            SAMLSignFactory.getInstance().sign(request, this.crypto);
            System.out.println("SamlSignSoapHandler -- SAML signed");
            System.out.println("SamlSignSoapHandler -- Replacing SAML in SOAPBody ...");
            Node nodeAfterSigning = request.toDOM();
            messageContext.getMessage().getSOAPBody().removeChild(nodeBeforeSigning);
            messageContext.getMessage().getSOAPBody().addDocument(nodeAfterSigning.getOwnerDocument());
            System.out.println("SamlSignSoapHandler -- SAML replaced");
        } catch (Exception e) {
            throw new STSClientException("SAML in SOAPBody could not be signed", e);
        }
        return true;
    }
    /**
     * 
     * @see javax.xml.rpc.handler.GenericHandler#handleResponse(javax.xml.rpc.handler.MessageContext)
     */
    public boolean handleResponse(MessageContext context) {
        return true;
    }
    /**
     * 
     * @see javax.xml.rpc.handler.GenericHandler#getHeaders()
     */
    public QName[] getHeaders() {
        return headers;
    }
    private SAMLSignedObject transformToSAML(Source request) {
        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            DOMResult result = new DOMResult();
            transformer.transform(request, result);
            Node samlNode = result.getNode().getFirstChild();
            if (samlNode.getLocalName().equals("Request")) {
                return new SAMLRequest((Element) samlNode);
            }
            if (samlNode.getLocalName().equals("response")) {
                return new SAMLResponse((Element) samlNode);
            }
            if (samlNode.getLocalName().equals("Assertion")) {
                return new SAMLAssertion((Element) samlNode);
            }
            throw new IllegalArgumentException("Not supported source (RootNode=" + samlNode.getLocalName() + ")");
        } catch (Exception e) {
            throw new STSClientException("Problem transforming source to SAML", e);
        }
    }
}
ClientHandlerChain.xml
<weblogic-wsee-clientHandlerChain xmlns="http://www.bea.com/ns/weblogic/90" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:j2ee="http://java.sun.com/xml/ns/j2ee">
    <handler>
        <j2ee:handler-name>SamlSignSoapHandler</j2ee:handler-name>
        <j2ee:handler-class>org.eh.sts.wsclient.handler.SamlSignSoapHandler</j2ee:handler-class>
        <j2ee:init-param>
            <j2ee:param-name>cryptoFile</j2ee:param-name>
            <j2ee:param-value>/crypto.properties</j2ee:param-value>
        </j2ee:init-param>
    </handler>
</weblogic-wsee-clientHandlerChain>

 

 

  • No labels