Tuesday, May 27, 2014

Invoke STS Service in WSO2 IS using CXF STS Client


Following guide describes how to invoke the STS Service in WSO2 Identity Server using a CXF STS client. The STS Client is configured via Spring.

As I have noticed, CXF STS client fails to invoke the STS service if you use the wso2carbon-sts wsdl to generate the client. So, I have followed a different approach.

Pre-requisites:


WSO2 Identity Server 4.6.0 or latter
Apache CXF 2.7.x

Instructions:


What I'm going to show you is using the CXF client via a simple stand-alone java class with Spring.

Configurations


1. First, you need to define the spring bean. Let's say the file is named wssec-sts-bean.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:cxf="http://cxf.apache.org/core"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:http="http://cxf.apache.org/transports/http/configuration"
       xsi:schemaLocation="
       http://cxf.apache.org/core   http://cxf.apache.org/schemas/core.xsd
       http://cxf.apache.org/transports/http/configuration            http://cxf.apache.org/schemas/configuration/http-conf.xsd
       http://www.springframework.org/schema/beans            http://www.springframework.org/schema/beans/spring-beans.xsd">
    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus>

<bean id="{http://ws.apache.org/axis2}wso2carbon-stsHttpsSoap12Endpoint.sts-client"
      class="org.apache.cxf.ws.security.trust.STSClient">
    <constructor-arg ref="cxf"/>
    <!--<property name="wsdlLocation" value="https://localhost:9443/services/wso2carbon-sts?wsdl"/>-->
    <property name="Location"
              value="https://localhost:9443/services/wso2carbon-sts.wso2carbon-stsHttpsSoap12Endpoint"/>
    <property name="serviceName" value="{http://ws.apache.org/axis2}wso2carbon-sts"/>
    <property name="endpointName" value="{http://ws.apache.org/axis2}wso2carbon-stsHttpsSoap12Endpoint"/>

    <property name="properties">
        <map>
            <entry key="ws-security.username" value="admin"/>
            <!--<entry key="ws-security.password" value="admin"/>-->
            <entry key="ws-security.callback-handler" value="com.cxf.sts.ClientCallbackHandler"/>
            <entry key="ws-security.encryption.properties"
                   value="bearer-client.properties"/>
            <entry key="ws-security.encryption.username" value="wso2carbon"/>
            <entry key="ws-security.sts.applies-to"
                   value="https://localhost:9453/services/echo"/>
        </map>
    </property>
</bean>
</beans>


NOTES: Please note that the wsdlLocation has NOT been set. Instead, we set the Location attribute which is the SOAP 1.2 STS https endpoint. Other attributes are the usual attributes that we normally set.

2. For the ws-security.encryption.properties property in the spring bean config, you have to  point to a properties file that has the encryption properties. The contents of bearer-client.properties would look like follows.

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=wso2carbon
org.apache.ws.security.crypto.merlin.keystore.alias=wso2carbon
org.apache.ws.security.crypto.merlin.keystore.file=/opt/wso2/wso2is-4.6.0/repository/resources/security/client-truststore.jks

3. We need one more configuration file to store the ws-policy of the STS service. First, you need to secure the STS service in IS as described in this documentation - https://docs.wso2.org/display/IS460/Securing+the+Security+Token+Service
Now, if you browse to the wso2carbon-sts WSDL, you will see the policy that is applied to your service. Extract the policy element from the WSDL. It starts with <wsp:Policy ...>. If you secure the service with UsernameToken, then the policy would like below. Place it in a file called sts.policy.xml.

<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UTOverTransport">
    <wsp:ExactlyOne  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" >
        <wsp:All>
            <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                <wsp:Policy>
                <sp:TransportToken>
                    <wsp:Policy>
                        <sp:HttpsToken RequireClientCertificate="false"></sp:HttpsToken>
                    </wsp:Policy>
                </sp:TransportToken>
                <sp:AlgorithmSuite>
                    <wsp:Policy>
                        <sp:Basic256></sp:Basic256>
                    </wsp:Policy>
                </sp:AlgorithmSuite>
                <sp:Layout>
                    <wsp:Policy>
                        <sp:Lax></sp:Lax>
                    </wsp:Policy>
                </sp:Layout>
                <sp:IncludeTimestamp></sp:IncludeTimestamp>
                </wsp:Policy>
            </sp:TransportBinding>
            <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                <wsp:Policy>
                    <sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"></sp:UsernameToken>
                </wsp:Policy>
            </sp:SignedSupportingTokens>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>

Now all the configuration files are in place. Now, we can go ahead, and write java code to retrieve the sts tokens.


STS Service Invocation


1. First, we need to set the trust-store of wso2 is as a system property since we are invoking a https endpoint. And, then, we will read the spring beans file.

System.setProperty("javax.net.ssl.trustStore",
        "/opt/wso2/wso2is-4.6.0/repository/resources/security/client-truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");

2. Next, we should read the spring bean file, wssec-sts-bean.xml, and get the bean we created.

ApplicationContext ctx = new FileSystemXmlApplicationContext(
        "classpath:wssec-sts-bean.xml");
STSClient sts = (STSClient) ctx.
        getBean("{http://ws.apache.org/axis2}wso2carbon-stsHttpsSoap12Endpoint.sts-client");



3. Now, we have the STSClient instance. We need to set some attributes to the client before we can request security tokens. One is the policy we just extracted from the WSDL.

//parse the ut policy xml, and get a DOM element
File f = new File("src/main/resources/sts.policy.xml");
Element stsPolicy = loadPolicy(f.getAbsolutePath());
sts.setPolicy(stsPolicy);

sts.setTokenType("http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0");
sts.setKeyType("http://schemas.xmlsoap.org/ws/2005/02/trust/Bearer");
sts.setSoap11(false);


4. Then, we can go ahead and request sts tokens.

SecurityToken samlToken = 
        sts.requestSecurityToken("http://localhost:9453/services/echo",
        "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT",
        "/Issue", null);

//convert the token dom element to string
String token = ((DOMImplementationLS) samlToken.getToken().getOwnerDocument().getImplementation()).
        createLSSerializer().writeToString(samlToken.getToken().getOwnerDocument());
System.out.println(token);

With these steps, you should be able to invoke STS service in WSO2 Identity Server using a CXF client. You can download the full source of this from here - http://goo.gl/Dpmjee

Testing 

 

1. Start WSO2 Identity Server 4.6.0.
2. Secure STS Service with UsernameToken as described in this documentation - https://docs.wso2.org/display/IS460/Securing+the+Security+Token+Service
3. Extract the client, cxf-sts-w-wso2-is.zip
4. Run the client as follows.

mvn clean install -Pclient



No comments:

Post a Comment