Showing posts with label wso2is. Show all posts
Showing posts with label wso2is. Show all posts

Wednesday, July 23, 2014

WSO2 IS SAML SSO using openSAML java library

In this blog post i will explain how to write a sample SSO service provider code using wso2is as the identity provider and openSAML as the supporting library to create SAML request.


First download the wso2IS from [1] and openSAML release from [2]. Then startup the wso2is. Create a new java web application project and import below jars from opensaml lib and lib/endorsed folders.


    serializer-2.10.0
    xalan-2.7.1
    xercesImpl-2.10.0
    xml-apis-2.10.0
    xmltooling-1.4.1
    xmlsec-1.5.6
    slf4j-api-1.7.5
    openws-1.5.1
    opensaml-2.6.1
    joda-time-2.2
    esapi-2.0.1
    commons-lang-2.6


Also include “axiom-api.jar” from apache axis project which has the UIDGenerator.java class.


Lets take our service provider name is “test.com” & our consumer landing page is “http://localhost:8080/test.com/home.jsp”. (You have to register a new service provider with above fields in wso2 identity server. To do so please refer [3] )


You can use below code to sent login & logout saml requests.




import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.common.SAMLVersion;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.AuthnContextClassRef;
import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.Issuer;
import org.opensaml.saml2.core.LogoutRequest;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.NameIDPolicy;
import org.opensaml.saml2.core.RequestAbstractType;
import org.opensaml.saml2.core.RequestedAuthnContext;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.SessionIndex;
import org.opensaml.saml2.core.impl.AuthnContextClassRefBuilder;
import org.opensaml.saml2.core.impl.AuthnRequestBuilder;
import org.opensaml.saml2.core.impl.IssuerBuilder;
import org.opensaml.saml2.core.impl.LogoutRequestBuilder;
import org.opensaml.saml2.core.impl.NameIDBuilder;
import org.opensaml.saml2.core.impl.NameIDPolicyBuilder;
import org.opensaml.saml2.core.impl.RequestedAuthnContextBuilder;
import org.opensaml.saml2.core.impl.SessionIndexBuilder;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallerFactory;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

public class SAMLConsumer {
    
    private static final Logger logger = LoggerFactory.getLogger(SAMLConsumer.class);
    private static boolean isBootstraped = false;

    private static final String ISSUER_URL = “test.com”;
    private static final String CONSUMER_URL = “http://localhost:8080/test.com/home.jsp”;

    private static final String NAME_SPACE_URI_ISSUER = “urn:oasis:names:tc:SAML:2.0:assertion”;
    private static final String NAME_SPACE_URI_AUTH = “urn:oasis:names:tc:SAML:2.0:protocol”;
    private static final String SAML_TRANSPORT_TYPE_PWD_PROTECTED = “urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport”;
    private static final String SAML_TRANSPORT_PROTOCOL_BINDING = “urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST”;
    
    private static final String LOCAL_NAME_ISSUER = “Issuer”;
    private static final String LOCAL_NAME_AUTHRQ = “AuthnRequest”;
    private static final String NAME_SPACE_PREFIX = “samlp”;
    private static final String AUTH_CNTXT_REF = “AuthnContextClassRef”;
    private static final String SAML = “saml”;
    
    public static String buildLoginRequest() throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException, MarshallingException, IOException  {
        logger.info(“started”);

        IssuerBuilder issuerBuilder = new IssuerBuilder();
        Issuer issuer = issuerBuilder.buildObject(NAME_SPACE_URI_ISSUER, LOCAL_NAME_ISSUER, “samlp”);
        issuer.setValue(ISSUER_URL);
        
        NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder();
        NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject();
        nameIdPolicy.setFormat(“urn:oasis:names:tc:SAML:2.0:nameid-format:persistent”);
        nameIdPolicy.setSPNameQualifier(“Isser”);
        nameIdPolicy.setAllowCreate(new Boolean(true));
        
        AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
        AuthnContextClassRef authnContextClassRef =
                                                    authnContextClassRefBuilder.buildObject(NAME_SPACE_URI_ISSUER,
                                                            AUTH_CNTXT_REF, SAML);
        authnContextClassRef.setAuthnContextClassRef(SAML_TRANSPORT_TYPE_PWD_PROTECTED);
        
        RequestedAuthnContextBuilder requestedAuthnContextBuilder =
                                                        new RequestedAuthnContextBuilder();
        RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
        requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
        requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
        
        DateTime issueInstant = new DateTime();
        AuthnRequestBuilder authnRequestBuilder = new AuthnRequestBuilder();
        AuthnRequest authnRequest = authnRequestBuilder.buildObject(NAME_SPACE_URI_AUTH, LOCAL_NAME_AUTHRQ, NAME_SPACE_PREFIX);
        authnRequest.setForceAuthn(new Boolean(false));
        authnRequest.setIsPassive(new Boolean(false));
        authnRequest.setIssueInstant(issueInstant);
        authnRequest.setProtocolBinding(SAML_TRANSPORT_PROTOCOL_BINDING);
        authnRequest.setAssertionConsumerServiceURL(CONSUMER_URL);
        authnRequest.setIssuer(issuer);
        authnRequest.setNameIDPolicy(nameIdPolicy);
        authnRequest.setRequestedAuthnContext(requestedAuthnContext);
        
        String authReqRandomId = Integer.toHexString(new Double(Math.random()).intValue());
        
        authnRequest.setID(authReqRandomId);
        authnRequest.setVersion(SAMLVersion.VERSION_20);
                
        return marshall(authnRequest);
        
    }
    
    public static String buildLogoutRequest(String user) throws MarshallingException, IOException {

        LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject();

        logoutReq.setID(createID());

        DateTime issueInstant = new DateTime();
        logoutReq.setIssueInstant(issueInstant);
        logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000));

        IssuerBuilder issuerBuilder = new IssuerBuilder();
        Issuer issuer = issuerBuilder.buildObject();
        issuer.setValue(ISSUER_URL);
        logoutReq.setIssuer(issuer);

        NameID nameId = new NameIDBuilder().buildObject();
        nameId.setFormat(“urn:oasis:names:tc:SAML:2.0:nameid-format:entity”);
        nameId.setValue(user);
        logoutReq.setNameID(nameId);

        SessionIndex sessionIndex = new SessionIndexBuilder().buildObject();
        sessionIndex.setSessionIndex(UIDGenerator.generateUID());
        logoutReq.getSessionIndexes().add(sessionIndex);

        logoutReq.setReason(“Single Logout”);

        return marshall(logoutReq);
    }
    
    private static String marshall(RequestAbstractType authnRequest) throws MarshallingException, IOException {
        doBootstrap();
        Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(authnRequest);
        Element authDOM = marshaller.marshall(authnRequest);
        
        
        StringWriter rspWrt = new StringWriter();
        XMLHelper.writeNode(authDOM, rspWrt);
        String requestMessage = rspWrt.toString();
        
        System.out.println(requestMessage);
                 
        Deflater deflater = new Deflater(Deflater.DEFLATED, true);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater);
        deflaterOutputStream.write(requestMessage.getBytes());
        deflaterOutputStream.close();
                 
        /* Encoding the compressed message */
        String encodedRequestMessage = Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES);
        String encodedAuthnRequest = URLEncoder.encode(encodedRequestMessage,”UTF-8”).trim();;
        
        return encodedAuthnRequest;
    }
    
    
    public static <T> T createSAMLObject(final Class<T> clazz) throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException {
         XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
        
         QName defaultElementName = (QName)clazz.getDeclaredField(“DEFAULT_ELEMENT_NAME”).get(null);
         Map<QName, XMLObjectBuilder> builderMap= builderFactory.getBuilders();
         System.out.println(“is nul “  + builderMap.get(defaultElementName));
        
        return null;
    }
    
    private static void doBootstrap() {
        if(!isBootstraped) {
            try {
                DefaultBootstrap.bootstrap();
                isBootstraped = true;
            } catch (ConfigurationException e) {
                logger.error(“Error calling bootstrap”, e);
            }
        }
    }
    
    public static Map<String, String> processResponse(String response) {

        XMLObject resp = null;
        
        try {
            resp = unmarshall(response);
        } catch (ConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnmarshallingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        return getResult(resp);
        
    }
    
    private static XMLObject unmarshall(String responseMessage) throws ConfigurationException,
                            ParserConfigurationException, SAXException, IOException, UnmarshallingException {
        
        doBootstrap();
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();
        
        byte[] base64DecodedResponse = Base64.decode(responseMessage.trim());
        
        System.out.println(new String(base64DecodedResponse));
        
        ByteArrayInputStream is = new ByteArrayInputStream(base64DecodedResponse);
        
        Document document = docBuilder.parse(is);
        Element element = document.getDocumentElement();
        UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
        Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element);
        return unmarshaller.unmarshall(element);
        
    }
    
    private static Map<String, String> getResult(XMLObject responseXmlObj) {

        if (responseXmlObj.getDOM().getNodeName().equals(“saml2p:LogoutResponse”)) {
            logger.error(“user logout”);
            return null;
        }

        Response response = (Response) responseXmlObj;
        logger.info(“SAML resp” + response);

        Assertion assertion = response.getAssertions().get(0);
        Map<String, String> resutls = new HashMap<String, String>();

        /*
         * If the request has failed, the IDP shouldn’t send an assertion.
         * SSO profile spec 4.1.4.2 <Response> Usage
         */
        if (assertion != null) {

            String subject = assertion.getSubject().getNameID().getValue();
            resutls.put(“Subject”, subject); // get the subject

            List<AttributeStatement> attributeStatementList = assertion.getAttributeStatements();

            if (attributeStatementList != null) {
                // we have received attributes of user
                Iterator<AttributeStatement> attribStatIter = attributeStatementList.iterator();
                while (attribStatIter.hasNext()) {
                    AttributeStatement statment = attribStatIter.next();
                    List<Attribute> attributesList = statment.getAttributes();
                    Iterator<Attribute> attributesIter = attributesList.iterator();
                    while (attributesIter.hasNext()) {
                        Attribute attrib = attributesIter.next();
                        Element value = attrib.getAttributeValues().get(0).getDOM();
                        String attribValue = value.getTextContent();
                        resutls.put(attrib.getName(), attribValue);
                    }
                }
            }
        }
        return resutls;
    }
    
    public static String createID() {

        byte[] bytes = new byte[20]; // 160 bit
        
        new Random().nextBytes(bytes);
        
        char[] charMapping = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’};

        char[] chars = new char[40];

        for (int i = 0; i < bytes.length; i++) {
            int left = (bytes[i] » 4) & 0x0f;
            int right = bytes[i] & 0x0f;
            chars[i * 2] = charMapping[left];
            chars[i * 2 + 1] = charMapping[right];
        }

        return String.valueOf(chars);
    }
    
}




[1] http://wso2.com/products/identity-server/


[2] https://wiki.shibboleth.net/confluence/display/OpenSAML/Home


[3] http://pavithramadurangi.blogspot.com/2013/09/saml-20-sso-with-wso2-is-450.html