Re: Re: [OpenSAML] signed SAMLRequest in SOAPMessage

468 views
Skip to first unread message

frederi...@smals.be

unread,
Apr 28, 2009, 10:08:46 AM4/28/09
to mace-open...@internet2.edu
I think found a workaround using a RPC SOAPHandler.
The samlRequest is sent 'unsigned' to the clientapplication building the soapmessage.
A soaphandler intervenes when the soap is allready generated.
It takes out the samlRequest in the soapbody, signs it with opensaml api, puts it back and gives control back to the client that sends the soap to the webservice.
That way, the xml in the soapbody is the same (including namespaces) as the xml at the time the signature is set.

It seems to work just fine.

Frederik

Chad La Joie

unread,
Apr 28, 2009, 10:26:59 AM4/28/09
to mace-open...@internet2.edu
Can you write this up with code examples? It's been asked a number of
times on the list and no one has ever shown real documentation on what
they have done such that other people can do it as well.

--
SWITCH
Serving Swiss Universities
--------------------------
Chad La Joie, Software Engineer, Net Services
Werdstrasse 2, P.O. Box, 8021 Zürich, Switzerland
phone +41 44 268 15 75, fax +41 44 268 15 68
chad....@switch.ch, http://www.switch.ch

frederi...@smals.be

unread,
Apr 30, 2009, 6:30:22 AM4/30/09
to mace-open...@internet2.edu
I don't know if I can send attachments so below is a printout of the SOAPHandler.

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 : begin ---
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);
}
}

}
--- SamlSignSoapHandler : end ---

to configure the handlerchain, I used the vendor specific xml-file but I think there are standard ones also (not sure though).

--- ClientHandlerChain.xml : begin ---

<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>

--- ClientHandlerChain.xml : end ---


Hope this can help,

Frederik Libert

Reply all
Reply to author
Forward
0 new messages