Example of an WE using WS-SEC

From GEANT2-JRA1 Wiki

There is available a Java library for WE developers, so you can add a security token in the request sent to a perfSONAR service. Its main purpose is that WEs are not WS-SEC aware. The class diagram of this library is:

Class diagram for the WE library


There is one helper class, org.perfsonar.client.base.authn.edugain.EduGAINFilterHelper, which has defined the method getAuthenticationAssertion(HttpSession session). This gets SAML assertions provided by an eduGAIN filter and get the Authentication one.

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
.
.
.
SAMLAssertion authStatementAssertion = 
     EduGAINFilterHelper.getAuthenticationAssertion(request.getSession());
.
.
.
}

Also, there is an interface which adds the security token in a message: org.perfsonar.client.base.authn.AuthNData. At the moment, there is only one implementation based on Apache Axis 1 Library, as perfSONAR is using it right now, and the class name is WSSAuthNData. The method addSAMLSTInMessage(...) needs the following parameters:

  • A SOAPBodeElement object, which contains generally a NMWG message (or what ever you want).
  • A PrivateKey object, containing a private key for signing the SAML assertion.
  • A X509Certificate object, containing the certificate for adding into the SAML assertion.
  • An String object, containing the URN which represents the perfSONAR resource that client are going to send the message to.
  • An String object, containing the URN which represents the client.
SOAPBodyElement requestMessage;
PrivateKey pk;
X509Certificate cert;
String cID;
String cID_pSR;
.
.
.
AuthNData authnData=AuthNDataFactory.getDefaultAuthNData();
SOAPEnvelope envelope = (SOAPEnvelope)authnData.addSAMLSTInMessage(requestMessage, 
     authStatementAssertion, pk, cert, cID_pSR, cID);

Java Servlet Example

This example is a servlet which gets all information provided by an eduGAIN filter, print out and send a message to a perfSONAR resource:

Web page generated by this example
Enlarge
Web page generated by this example

And the Java code is:

package es.rediris.perfsonar;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Enumeration;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
 
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.message.SOAPBodyElement;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.ws.security.util.Base64;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.opensaml.SAMLAssertion;
import org.perfsonar.client.commons.authn.AuthNData;
import org.perfsonar.client.commons.authn.AuthNDataFactory;
import org.perfsonar.client.commons.authn.edugain.EduGAINFilterHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
 
public class EduGAINed_ASTest extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
	public static final String END_POINT_PARAM="endPoint";
	public static final String QUERY_FILE_PARAM="queryFile";
	public static final String PRIV_KEY_PARAM="privateKey";
	public static final String PUBLIC_KEY_PARAM="publicKey";
	public static final String COMPONENT_ID_PARAM="componentId";
	public static final String COMPONENT_ID_pSR_PARAM="componentId_pSR";
 
	private X509Certificate certificate=null;
	private PrivateKey privKey=null;
 
	/* (non-Java-doc)
	 * @see javax.servlet.http.HttpServlet#HttpServlet()
	 */
	public EduGAINed_ASTest() {
		super();
        // add the security provider
    	BouncyCastleProvider bcp = new BouncyCastleProvider();
        java.security.Security.addProvider((Provider)bcp);
	}   	
	
	private PrivateKey getPrivateKey() {
		if (privKey!=null)
			return privKey;
		try {
	        BufferedReader in = new BufferedReader(new FileReader(getServletContext().getRealPath(getServletConfig().getInitParameter(EduGAINed_LSClient.PRIV_KEY_PARAM))));
	        String str;
	        String previousStr="";
	        String data="";
	        in.readLine();
	        while ((str = in.readLine()) != null) {
	            data+=previousStr;
	            previousStr=str+"\n";
	        }
	        in.close();
	        
	        byte[] bytes=Base64.decode(data);
	        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
	        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
	        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
	        return privateKey;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public X509Certificate getCertificate() {
		if (certificate!=null)
			return certificate;
		try {
			 FileInputStream is = new FileInputStream(new File(getServletContext().
                           getRealPath(getServletConfig().getInitParameter(EduGAINed_LSClient.PUBLIC_KEY_PARAM))));
			    
			 CertificateFactory cf = CertificateFactory.getInstance("X.509");
			 X509Certificate cert = (X509Certificate)cf.generateCertificate(is);
			 return cert;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	private Document getQueryMessage() {
		try {
			Document request = null;
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
 
			DocumentBuilder builder = factory.newDocumentBuilder();
			request = builder.parse(new File(getServletContext().
                               getRealPath(getServletConfig().getInitParameter(EduGAINed_ASTest.QUERY_FILE_PARAM))));
			
			return request;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/* (non-Java-doc)
	 * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter writer = response.getWriter();
 
		try {
			// prepare to call - set service elements 
			Service serviceAxis = new Service();
			Call call = (Call)serviceAxis.createCall();
			call.setTargetEndpointAddress(new URL(getServletConfig().getInitParameter(EduGAINed_ASTest.END_POINT_PARAM)));
			call.setOperationName(new QName("http://soapinterop.org/","submit"));
 
			writer.println("<html>");
			writer.println("<head>");
 
			writer.println("<title>eduGAINed ASTest</title>");
			writer.println("<link href='style.css' rel='stylesheet' type='text/css'>");
			writer.println("</head>");
			writer.println("<body>");
 
			writer.println("<div><h3>Configuration:</h3></div>");
			writer.println("<ul>");
			writer.println("<li><span class='attrName'>End point</span>: "+getServletConfig().
                                     getInitParameter(EduGAINed_ASTest.END_POINT_PARAM)+"</li>");
			writer.println("<li><span class='attrName'>XML Message</span>: "+getServletConfig().
                                     getInitParameter(EduGAINed_ASTest.QUERY_FILE_PARAM)+"</li>");
			writer.println("<li><span class='attrName'>Private Key</span>: "+getServletConfig().
                                     getInitParameter(EduGAINed_ASTest.PRIV_KEY_PARAM)+"</li>");
			writer.println("<li><span class='attrName'>Certificate</span>: "+getServletConfig().
                                     getInitParameter(EduGAINed_ASTest.PUBLIC_KEY_PARAM)+"</li>");
			writer.println("<li><span class='attrName'>Component ID</span>: "+getServletConfig().
                                     getInitParameter(EduGAINed_ASTest.COMPONENT_ID_PARAM)+"</li>");
			writer.println("<li><span class='attrName'>Component ID pSR</span>: "+getServletConfig().
                                     getInitParameter(EduGAINed_ASTest.COMPONENT_ID_pSR_PARAM)+"</li>");
			writer.println("</ul>");
						
			SAMLAssertion authStatementAssertion=EduGAINFilterHelper.getAuthenticationAssertion(request.getSession());
						
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
 
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document authStatementAssertionDoc=builder.newDocument();
			Node elem=authStatementAssertion.toDOM(authStatementAssertionDoc);
			authStatementAssertionDoc.appendChild(elem);
			
			if (authStatementAssertion==null) {
				writer.println("<h3>No SAML authentication statement found!<h3>");
			}
			else {
				writer.println("<div><h3>SAML authentication statement:</h3></div>");
				String data=StringEscapeUtils.escapeXml(formatDocument(authStatementAssertionDoc));
				data=data.replaceAll("\n", "<br />");
				data=data.replaceAll("  ", "&nbsp;&nbsp;");
				writer.println("<div class='infoXML'>"+data+"</div>");
				
				Document docQuery=getQueryMessage();
				writer.println("<div><h3>Sent query:</h3></div>");
				data=StringEscapeUtils.escapeXml(formatDocument(docQuery));
				data=data.replaceAll("\n", "<br />");
				data=data.replaceAll("  ", "&nbsp;&nbsp;");
				writer.println("<div class='infoXML'>"+data+"</div>");
								
	            SOAPBodyElement requestMessage = 
	                new SOAPBodyElement(docQuery.getDocumentElement());
 
	            String cID=getServletConfig().getInitParameter(EduGAINed_ASTest.COMPONENT_ID_PARAM);
	            String cID_pSR=getServletConfig().getInitParameter(EduGAINed_ASTest.COMPONENT_ID_pSR_PARAM);
	            AuthNData authnData=AuthNDataFactory.getDefaultAuthNData();
	            SOAPEnvelope envelope = (SOAPEnvelope)authnData.addSAMLSTInMessage(requestMessage, 
                                     authStatementAssertion,getPrivateKey(), getCertificate(), cID_pSR,cID);
	            Document wssDoc = envelope.getAsDocument();
				writer.println("<div><h3>Sent SOAP query:</h3></div>");
				data=StringEscapeUtils.escapeXml(formatDocument(wssDoc));
				data=data.replaceAll("\n", "<br />");
				data=data.replaceAll("  ", "&nbsp;&nbsp;");
				writer.println("<div class='infoXML'>"+data+"</div>");
 
				 // call on the end point
				Object resultObject = call.invoke(envelope);
				
	            SOAPEnvelope envelopeResult=(SOAPEnvelope)resultObject;
	            SOAPBodyElement resultSBE= envelopeResult.getFirstBody();
	            Document result = resultSBE.getAsDocument();
				writer.println("<div><h3>Received response:</h3></div>");
				data=StringEscapeUtils.escapeXml(formatDocument(result));
				data=data.replaceAll("\n", "<br />");
				data=data.replaceAll("  ", "&nbsp;&nbsp;");
				writer.println("<div class='infoXML'>"+data+"</div>");
			} 
 
			writer.println("</body>");
			writer.println("</html>");
		} catch (Exception e) {
			writer.println("<div><h3>ERROR:</h3></div>");
			writer.println("<div class='infoXML'>"+e.toString()+"</div>");
		}
	}  
	
	private String formatDocument(Document doc) throws IOException {
		StringWriter sw=new StringWriter();
        OutputFormat format = new OutputFormat( doc );
        format.setIndent(4);
        format.setIndenting(true);
        format.setLineSeparator("\n");
 
        XMLSerializer serial = new XMLSerializer(sw, format );
        serial.asDOMSerializer();
        serial.serialize( doc.getDocumentElement() );
 
        return sw.toString();
	}
}
Personal tools