Saturday 27 September 2014

WebService security and policy implementation using CXF 2.5.2 and Jboss 5.0.1

A web service is a method of communication between multiple devices over the World Wide Web. Using Web Services we can publish application's functions to everyone.In my previous blog I have explained with example how to create simple jaxws webservice application using apache cxf Jaxws Web Services example using CXF .

This Blog instruction will help us to create a sample secure jaxws webservice using apahe cxf and webservice policies
(http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd).


Please note I choose wsdl and spring xml configuration to secure jaxws webservice application so it will be easy for user to switch on/off and configure security features without modifying service interface and implementation classes.

  • Pre-requsite
  • Instructions assume that jdk,jboss,cxf is installed without any error.

  • Implemenation instructions
    1. please open existing project that successfully created by using previous blog instructions previous blog instructions . Now we will modify existing project and secure the web application.
    2. Open Sample.wsdl under WEB-INF\wsdl\Sample.wsdl and add following 3 options is wsdl.
      • Username token with plain text
      This is first option . In this case user need to provide username and password.
       
      <!--  Option 1 -->
          <!-- UsernameTokenWithPlainTextPassword ## Simple username token policy plain text -->
           <!--  -->
              <wsp:Policy wsu:Id="UsernameTokenWithPlainTextPassword"
                 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                          xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:ExactlyOne>
                   <wsp:All>
                          <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                           <wsp:Policy>
                                  <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient" />
                              </wsp:Policy>
                          </sp:SupportingTokens>
                      </wsp:All>
                  </wsp:ExactlyOne>
               </wsp:Policy>
      
      • Username token with password hash
      This is second option . In this case user need to provide username and password.
       
      <!--  Option 2 -->
          <!-- UsernameTokenWithTimestampNoncePasswordHash ## UsernameToken With Hash -->
        <wsp:Policy wsu:Id="UsernameTokenWithTimestampNoncePasswordHash"
                      xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                      xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                      xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
         <wsp:ExactlyOne>
          <wsp:All>
                        <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                               <wsp:Policy>
                                       <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                      <sp:HashPassword/>
                               </wsp:Policy>
                               </sp:UsernameToken>
                              </wsp:Policy>
                        </sp:SupportingTokens>
          </wsp:All>
         </wsp:ExactlyOne>
        </wsp:Policy>
      
      • SignEncryption
      This is third option . In this case user need to provide public key information. Policy for first signing and then encrypting all messages, with the certificate included in the message from client to server but only a thumb print on messages from the server to the client. we can say it is ws-trust feature.
      <!--  Option 3 -->
           <!-- SignEncryption ## Policy for first signing and then encrypting all messages, with the certificate
               included in the message from client to server but only a thumb print on messages from
               the server to the client. -->
           
             <wsp:Policy wsu:Id="SignEncryption"
                         xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                         xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
              <wsp:ExactlyOne>
                  <wsp:All>
                      <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <wsp:Policy>
                              <sp:InitiatorToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:InitiatorToken>
                              <sp:RecipientToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:RecipientToken>
                              <sp:AlgorithmSuite>
                                  <wsp:Policy>
                                      <sp:TripleDesRsa15/>
                                  </wsp:Policy>
                              </sp:AlgorithmSuite>
                              <sp:Layout>
                                  <wsp:Policy>
                                      <sp:Strict/>
                                  </wsp:Policy>
                              </sp:Layout>
                              <sp:IncludeTimestamp/>
                              <sp:OnlySignEntireHeadersAndBody/>
                          </wsp:Policy>
                      </sp:AsymmetricBinding>
                      <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:SignedParts>
                      <sp:EncryptedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:EncryptedParts>
                  </wsp:All>
              </wsp:ExactlyOne>
          </wsp:Policy>
      
      After adding above three security policy in wsdl we will use them one by one by referring the policy in wsdl:binding section. eg .
        
      <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameTokenWithPlainTextPassword" />
      
      After adding three security policy wsdl will look like following.
      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      <wsdl:definitions name="BSGLifecycleServices"
      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
      xmlns:tns="http://poc.kaustuv.com/ws/service/greetings"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://poc.kaustuv.com/ws/service/greetings">
       <!--  Option 1 -->
          <!-- UsernameTokenWithPlainTextPassword ## Simple username token policy plain text -->
           <!--  -->
              <wsp:Policy wsu:Id="UsernameTokenWithPlainTextPassword"
                 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                          xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:ExactlyOne>
                   <wsp:All>
                          <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                           <wsp:Policy>
                                  <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient" />
                              </wsp:Policy>
                          </sp:SupportingTokens>
                      </wsp:All>
                  </wsp:ExactlyOne>
               </wsp:Policy>
      
       <!--  Option 2 -->
          <!-- UsernameTokenWithTimestampNoncePasswordHash ## UsernameToken With Hash -->
          <!--
        <wsp:Policy wsu:Id="UsernameTokenWithTimestampNoncePasswordHash"
                      xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                      xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                      xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
         <wsp:ExactlyOne>
          <wsp:All>
                        <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                               <wsp:Policy>
                                       <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                      <sp:HashPassword/>
                               </wsp:Policy>
                               </sp:UsernameToken>
                              </wsp:Policy>
                        </sp:SupportingTokens>
          </wsp:All>
         </wsp:ExactlyOne>
        </wsp:Policy>
          -->
      
       <!--  Option 3 -->
           <!-- SignEncryption ## Policy for first signing and then encrypting all messages, with the certificate
               included in the message from client to server but only a thumb print on messages from
               the server to the client. -->
           <!--
             <wsp:Policy wsu:Id="SignEncryption"
                         xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                         xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
              <wsp:ExactlyOne>
                  <wsp:All>
                      <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <wsp:Policy>
                              <sp:InitiatorToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:InitiatorToken>
                              <sp:RecipientToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:RecipientToken>
                              <sp:AlgorithmSuite>
                                  <wsp:Policy>
                                      <sp:TripleDesRsa15/>
                                  </wsp:Policy>
                              </sp:AlgorithmSuite>
                              <sp:Layout>
                                  <wsp:Policy>
                                      <sp:Strict/>
                                  </wsp:Policy>
                              </sp:Layout>
                              <sp:IncludeTimestamp/>
                              <sp:OnlySignEntireHeadersAndBody/>
                          </wsp:Policy>
                      </sp:AsymmetricBinding>
                      <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:SignedParts>
                      <sp:EncryptedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:EncryptedParts>
                  </wsp:All>
              </wsp:ExactlyOne>
          </wsp:Policy>
         -->
          <wsdl:types>
              <xsd:schema targetNamespace="http://poc.kaustuv.com/ws/service/greetings">
                  <xsd:element name="GreetingsRequest">
                      <xsd:complexType>
                          <xsd:sequence>
                              <xsd:element name="name" type="xsd:string" />
                          </xsd:sequence>
                      </xsd:complexType>
                  </xsd:element>
                  <xsd:element name="GreetingsResponse">
                      <xsd:complexType>
                          <xsd:sequence>
                              <xsd:element name="message" type="xsd:string" />
                          </xsd:sequence>
                      </xsd:complexType>
                  </xsd:element>
              </xsd:schema>
          </wsdl:types>
          <wsdl:message name="GreetingsReq">
              <wsdl:part element="tns:GreetingsRequest" name="parameterIn" />
          </wsdl:message>
          <wsdl:message name="GreetingsRes">
              <wsdl:part element="tns:GreetingsResponse" name="parameterOut" />
          </wsdl:message>
          <wsdl:portType name="Greetings">
              <wsdl:operation name="Greetings">
                  <wsdl:input message="tns:GreetingsReq" />
                  <wsdl:output message="tns:GreetingsRes" />
              </wsdl:operation>
          </wsdl:portType>
          <wsdl:binding name="GreetingsSOAPBinding" type="tns:Greetings">
              <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameTokenWithPlainTextPassword" />
        <!-- wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameTokenWithTimestampNoncePasswordHash" /-->
        <!-- wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#SignEncryption" /-->
              <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
              <wsdl:operation name="Greetings">
                  <soap:operation soapAction="Greetings" />
                  <wsdl:input>
                      <soap:body use="literal" />
                  </wsdl:input>
                  <wsdl:output>
                      <soap:body use="literal" />
                  </wsdl:output>
              </wsdl:operation>
          </wsdl:binding>
          <wsdl:service name="GreetingsService">
              <wsdl:port name="GreetingsPort" binding="tns:GreetingsSOAPBinding">
                  <soap:address location="http://www.kaustuv.com/ws/service/" />
              </wsdl:port>
          </wsdl:service>
      </wsdl:definitions>
      
      It is strongly recommended to use above three options individually. Eg. suppose we are planning to use security option 1 then comment out other two security options and refer option 1 in wsd:binding section.
    3. Create a new package "com.kaustuv.poc.ws.security"
      • create a new CallbackHandler class ServerPasswordCallback under package "com.kaustuv.poc.ws.security". Add following code snippet.

      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package com.kaustuv.poc.ws.security;
      
      import java.io.IOException;
      
      import javax.security.auth.callback.Callback;
      import javax.security.auth.callback.CallbackHandler;
      import javax.security.auth.callback.UnsupportedCallbackException;
      
      import org.apache.ws.security.WSPasswordCallback;
      
      /**
       * This class is responsible to implement web service security features.
       *
       * @author KMaji
       *
       */
      public class ServerPasswordCallback implements CallbackHandler {
      
       private SignatureInfoBean signatureInfo;
      
       /**
        * Getter for the property signatureInfo.
        *
        * @return Returns the value of signatureInfo.
        */
       public SignatureInfoBean getSignatureInfo() {
        return signatureInfo;
       }
      
       /**
        * Setter for the property signatureInfo.
        *
        * @param signatureInfo
        *            The new value of signatureInfo.
        */
       public void setSignatureInfo(SignatureInfoBean signatureInfo) {
        this.signatureInfo = signatureInfo;
       }
      
       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback wsPasswordCallback = null;
        String password = null;
        for (int count = 0; count < callbacks.length; count++) {
         wsPasswordCallback = (WSPasswordCallback) callbacks[count];
         // String identifier = wsPasswordCallback.getIdentifier();
         switch (wsPasswordCallback.getUsage()) {
          case WSPasswordCallback.USERNAME_TOKEN :
           try {
            password = "password";
           } catch (Exception exp) {
            exp.printStackTrace();
           }
           wsPasswordCallback.setPassword(password);
           break;
          case WSPasswordCallback.DECRYPT :
           wsPasswordCallback.setPassword(signatureInfo.getSignatureServiceKeyPassword());
           break;
          case WSPasswordCallback.SIGNATURE :
           wsPasswordCallback.setPassword(signatureInfo.getSignatureServiceKeyPassword());
           break;
         }
        }
       }
      }
      

      • Create SignatureInfoBean class under package "com.kaustuv.poc.ws.security". Add following code snippet

      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package com.kaustuv.poc.ws.security;
      
      import java.io.Serializable;
      
      public class SignatureInfoBean implements Serializable {
      
       private static final long serialVersionUID = -5749465533676763004L;
      
       public SignatureInfoBean() {
       }
      
       private String signatureServiceKey = null;
       private String signatureServiceKeyPassword = null;
      
       /**
        * Getter for the property signatureServiceKey.
        *
        * @return Returns the value of signatureServiceKey.
        */
       public String getSignatureServiceKey() {
        return signatureServiceKey;
       }
      
       /**
        * Setter for the property signatureServiceKey.
        *
        * @param signatureServiceKey
        *            The new value of signatureServiceKey.
        */
       public void setSignatureServiceKey(String signatureServiceKey) {
        this.signatureServiceKey = signatureServiceKey;
       }
      
       /**
        * Getter for the property signatureServiceKeyPassword.
        *
        * @return Returns the value of signatureServiceKeyPassword.
        */
       public String getSignatureServiceKeyPassword() {
        return signatureServiceKeyPassword;
       }
      
       /**
        * Setter for the property signatureServiceKeyPassword.
        *
        * @param signatureServiceKeyPassword
        *            The new value of signatureServiceKeyPassword.
        */
       public void setSignatureServiceKeyPassword(String signatureServiceKeyPassword) {
        this.signatureServiceKeyPassword = signatureServiceKeyPassword;
       }
      }
      

    4. Open cxf-beans.xml under WEB-INF/conf and modify as following.

    5. <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:jaxws="http://cxf.apache.org/jaxws"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
          http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
          <import resource="classpath:META-INF/cxf/cxf.xml" />
          <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
          <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
          <bean id="greeting" class="com.kaustuv.poc.ws.service.greetings.GreetingsImpl" />
          <jaxws:endpoint xmlns:tns="http://poc.kaustuv.com/ws/service/greetings" id="greetings"
              implementor="#greeting" wsdlLocation="WEB-INF/wsdl/Sample.wsdl" endpointName="tns:GreetingsPort"
              serviceName="tns:GreetingsService" address="/Greetings">
              <jaxws:features>
                  <bean class="org.apache.cxf.feature.LoggingFeature" />
              </jaxws:features>
              <!-- usernameToken option 1 and 2 -->
              <!--  -->
              <jaxws:properties>
                  <entry key="schema-validation-enabled" value="true" />
                  <entry key="ws-security.callback-handler">
                      <ref bean="serverPasswordCallback" />
                  </entry>
              </jaxws:properties>
             
               <!-- Sign Encryption option 3 only-->
               <!-- 
              <jaxws:properties>
                  <entry key="ws-security.signature.properties" value="file:D:/workspace/jaxws-security-example/WebContent/WEB-INF/keys/serviceKeystore.properties"/>
                  <entry key="ws-security.signature.username" value="myservicekey"/>
                  <entry key="ws-security.encryption.username" value="useReqSigCert"/>
                  <entry key="ws-security.callback-handler">
                      <ref bean="serverPasswordCallback" />
                  </entry>
              </jaxws:properties>
               -->
          </jaxws:endpoint>
      </beans>
      
      In above xml there are two jaxws:properties configuration added for three security option that we already mentioned security options in wsdl. For option 1 and 2 written in wsdl we must enable following by following snippet in cxf-beans.xml.
           
      <jaxws:properties>
       <entry key="schema-validation-enabled" value="true" />
       <entry key="ws-security.callback-handler">
        <ref bean="serverPasswordCallback" />
       </entry>
      </jaxws:properties>
      
      For option 3 written in wsdl we must enable corresponding jaxws:properties by following snippet in cxf-beans.xml .
            
      <jaxws:properties>
       <entry key="ws-security.signature.properties" value="file:D:/workspace/jaxws-security-example/WebContent/WEB-INF/keys/serviceKeystore.properties"/>
       <entry key="ws-security.signature.username" value="myservicekey"/>
       <entry key="ws-security.encryption.username" value="useReqSigCert"/>
       <entry key="ws-security.callback-handler">
        <ref bean="serverPasswordCallback" />
       </entry>
      </jaxws:properties>
      
      Please notice we can not keep both jaxws:properties in cxf-beans.xml .
      • Create security-beans.xml under WEB-INF/conf and add following snippet.

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
          <bean id="serverPasswordCallback" class="com.kaustuv.poc.ws.security.ServerPasswordCallback">
              <property name="signatureInfo" ref="signatureInfo" />
          </bean>
          <bean id="signatureInfo" class="com.kaustuv.poc.ws.security.SignatureInfoBean">
              <property name="signatureServiceKey" value="myservicekey" />
              <property name="signatureServiceKeyPassword" value="skpass" />
          </bean>
      </beans>
      
      In abve xml we created two bean one is serverPasswordCallback and another one is signatureInfo both are used for security implementation.
    6. create folder name keys under WEB-INF/
      • create following private and public key and certificates under WEB-INF/keys
      key maker steps
      keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 730 -alias myservicekey -keypass skpass -storepass sspass -keystore serviceKeystore.jks -dname "cn=localhost"
      keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 730 -alias myclientkey  -keypass ckpass -storepass cspass -keystore clientKeystore.jks -dname "cn=clientuser"
      keytool -export -rfc -keystore clientKeystore.jks -storepass cspass -alias myclientkey -file MyClient.cer
      keytool -import -trustcacerts -keystore serviceKeystore.jks -storepass sspass -alias myclientkey -file MyClient.cer -noprompt
      keytool -export -rfc -keystore serviceKeystore.jks -storepass sspass -alias myservicekey -file MyService.cer
      keytool -import -trustcacerts -keystore clientKeystore.jks -storepass cspass -alias myservicekey -file MyService.cer -noprompt
      

      • create serviceKeystore.properties under WEB-INF/keys and add following snippet

      org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
      org.apache.ws.security.crypto.merlin.keystore.file=D:/workspace/jaxws-security-example/WebContent/WEB-INF/keys/serviceKeystore.jks
      org.apache.ws.security.crypto.merlin.keystore.password=sspass
      org.apache.ws.security.crypto.merlin.keystore.type=jks
      org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
      
      Now for security option 3 server side is secured with private key and to access secured webservice then user need to provide public key. Once user is authorized by public key then user will be able to access webservice.
    7. open web.xml and modify as following
    8. This step is optional. We are doing it for just to be in safely sync with the examples.
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee           http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
        <display-name>jaxws-security-example</display-name>
         <servlet>
              <description>Apache CXF Endpoint</description>
              <display-name>cxf</display-name>
              <servlet-name>CXFServlet</servlet-name>
              <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
          </servlet>
          <servlet-mapping>
              <servlet-name>CXFServlet</servlet-name>
              <url-pattern>/*</url-pattern>
          </servlet-mapping> 
          <listener>
           <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
         </listener>
         <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>WEB-INF/conf/*beans.xml</param-value>
         </context-param>
      </web-app>
      

    9. Export and package this as war(jaws-security-example.war).Destination path %JBOSS_HOME%\server\default\deploy
    10. Note:If we do not modify the web.xml then we do not need to change the war name.

    11. Open command prompt and go to directory %JBOSS_HOME\bin .
    12. Execute run.bat -c default -b 0.0.0.0
    13. click following link for our secure jaxws webservice project.
    14. http://localhost:8080/jaxws-security-example/Greetings?wsdl
      Using webservice policy is very transparent for webservice client to understand what kind of security is associated to service by readin published webservice wsdl link. Check highlighted part in following image.

        Post Development testing


    1. Option 1 security test.
    2. Following class will be used to test the username token policy.
      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package client;
      
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.util.Map;
      
      import javax.xml.namespace.QName;
      import javax.xml.ws.BindingProvider;
      import javax.xml.ws.Service;
      
      import org.apache.cxf.endpoint.Client;
      import org.apache.cxf.endpoint.Endpoint;
      import org.apache.cxf.frontend.ClientProxy;
      import org.apache.cxf.interceptor.LoggingInInterceptor;
      import org.apache.cxf.interceptor.LoggingOutInterceptor;
      import org.apache.log4j.BasicConfigurator;
      import org.apache.log4j.Level;
      import org.apache.log4j.Logger;
      
      import com.kaustuv.poc.ws.service.greetings.Greetings;
      import com.kaustuv.poc.ws.service.greetings.GreetingsRequest;
      import com.kaustuv.poc.ws.service.greetings.GreetingsResponse;
      
      /**
       * @author KMaji
       *
       */
      public class TestUserNameToken {
      
       private static final Logger LOG = Logger.getLogger(TestUserNameToken.class.getName());
      
       /**
        * @param args
        * @throws MalformedURLException
        */
       public static void main(String[] args) throws MalformedURLException {
        Service service = Service.create(new URL("http://localhost:8080/jaxws-security-example/Greetings?wsdl"),
          new QName("http://poc.kaustuv.com/ws/service/greetings",
          "GreetingsService"));
          
        Greetings greetings = service.getPort(Greetings.class);
        Client client = ClientProxy.getClient(greetings);
        Endpoint endPoint = client.getEndpoint();
        endPoint.getOutInterceptors().add(new LoggingOutInterceptor());
        endPoint.getInInterceptors().add(new LoggingInInterceptor());
      
        Map<String, Object> ctx = ((BindingProvider) greetings).getRequestContext();
      
        ctx.put("ws-security.username", "demo");
        ctx.put("ws-security.password", "password");
      
        GreetingsRequest gReq = new GreetingsRequest();
        gReq.setName("kaustuv");
      
        GreetingsResponse gRes = null;
        try {
         gRes = greetings.greetings(gReq);
        } catch (Throwable e) {
         LOG.error(e.getMessage(), e.getCause());
        }
        LOG.info("Response # " + gRes.getMessage());
       }
      }
      
      ID: 2
      Address: http://localhost:8080/jaxws-security-example/Greetings
      Encoding: UTF-8
      Http-Method: POST
      Content-Type: text/xml; charset=UTF-8
      Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[745], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["Greetings"], user-agent=[Apache CXF 2.5.2]}
      Payload: demopasswordkaustuv
      --------------------------------------
      18:58:33,784 INFO  [STDOUT] com.kaustuv.poc.ws.service.greetings.GreetingsRequest@74d345b8
      INFO: Outbound Message
      ---------------------------
      ID: 2
      Encoding: UTF-8
      Content-Type: text/xml
      Headers: {}
      Payload: kaustuv
      --------------------------------------
      
      In above console log we saw the username and password (plain text) is wrapped in soap:header section.

    3. Option 2 security test.
    4. For testing option 2 we will use the same class which we used to test username token plain text.
      ID: 3
      Address: http://localhost:8080/jaxws-security-example/Greetings
      Encoding: UTF-8
      Http-Method: POST
      Content-Type: text/xml; charset=UTF-8
      Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[977], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["Greetings"], user-agent=[Apache CXF 2.5.2]}
      Payload: demoZQl7x4is11VIDiZXxZex7jAPe4g=/okFkV98Off1m8VYQs0BXg==2014-09-27T13:36:29.140Zkaustuv
      --------------------------------------
      19:06:29,471 INFO  [STDOUT] com.kaustuv.poc.ws.service.greetings.GreetingsRequest@46d4db6
      INFO: Outbound Message
      ---------------------------
      ID: 3
      Encoding: UTF-8
      Content-Type: text/xml
      Headers: {}
      Payload: kaustuv
      --------------------------------------
      
      In above console log we saw the username and password (encrypted) is wrapped in soap:header section.

    5. Option 3 security test.
    6. Create clientcallback class for client application for encrypting message.
      /**
       *
       * Copyright (c) Kaustuv Maji , 2014
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       *
       */
      package client;
      
      import java.io.IOException;
      import java.util.HashMap;
      import java.util.Map;
      
      import javax.security.auth.callback.Callback;
      import javax.security.auth.callback.CallbackHandler;
      import javax.security.auth.callback.UnsupportedCallbackException;
      
      import org.apache.ws.security.WSPasswordCallback;
      
      /**
       * @author KMaji
       *
       */
      public class SignEncrClientCallback implements CallbackHandler {
      
       private Map<String, String> passwords = new HashMap<String, String>();
      
       public SignEncrClientCallback() {
        passwords.put("myclientkey", "ckpass");
       }
      
       @Override
       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
         WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
         String pass = passwords.get(pc.getIdentifier());
         if (pass != null) {
          pc.setPassword(pass);
          return;
         }
        }
       }
      }
      
      Following class is main test class for security option 3.
      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package client;
      
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.util.Map;
      
      import javax.xml.namespace.QName;
      import javax.xml.ws.BindingProvider;
      import javax.xml.ws.Service;
      
      import org.apache.cxf.endpoint.Client;
      import org.apache.cxf.endpoint.Endpoint;
      import org.apache.cxf.frontend.ClientProxy;
      import org.apache.cxf.interceptor.LoggingInInterceptor;
      import org.apache.cxf.interceptor.LoggingOutInterceptor;
      import org.apache.log4j.BasicConfigurator;
      import org.apache.log4j.Level;
      import org.apache.log4j.Logger;
      
      import com.kaustuv.poc.ws.service.greetings.Greetings;
      import com.kaustuv.poc.ws.service.greetings.GreetingsRequest;
      import com.kaustuv.poc.ws.service.greetings.GreetingsResponse;
      
      /**
       * @author KMaji
       *
       */
      public class SignEncryptionClient {
      
       /**
        * @param args
        * @throws MalformedURLException
        */
       public static void main(String[] args) throws MalformedURLException {
        Service service = Service.create(new URL("http://localhost:8080/jaxws-security-example/Greetings?wsdl"),
          new QName("http://poc.kaustuv.com/ws/service/greetings",
          "GreetingsService"));
        Logger LOG = Logger.getLogger(SignEncryptionClient.class.getName());
        Greetings greetings = service.getPort(Greetings.class);
        Client client = ClientProxy.getClient(greetings);
        Endpoint endPoint = client.getEndpoint();
        endPoint.getOutInterceptors().add(new LoggingOutInterceptor());
        endPoint.getInInterceptors().add(new LoggingInInterceptor());
      
        Map<String, Object> ctx = ((BindingProvider) greetings).getRequestContext();
      
        ctx.put("ws-security.signature.properties", "clientKeystore.properties");
        ctx.put("ws-security.signature.username", "myclientkey");
        ctx.put("ws-security.encryption.properties", "clientKeystore.properties");
        ctx.put("ws-security.encryption.username", "myservicekey");
        ctx.put("ws-security.callback-handler", "client.SignEncrClientCallback");
      
        GreetingsRequest gReq = new GreetingsRequest();
        gReq.setName("kaustuv");
      
        GreetingsResponse gRes = null;
        try {
         gRes = greetings.greetings(gReq);
         LOG.info("Response #" + gRes.getMessage());
        } catch (Exception e) {
         e.printStackTrace();
        }
       }
      }
      
      ID: 4
      Address: http://localhost:8080/jaxws-security-example/Greetings
      Encoding: UTF-8
      Http-Method: POST
      Content-Type: text/xml; charset=UTF-8
      Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["Greetings"], transfer-encoding=[chunked], user-agent=[Apache CXF 2.5.2]}
      Payload: MIIBoTCCAQqgAwIBAgIEUVm7NzANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDEwpjbGllbnR1c2VyMB4XDTEzMDQwMTE2NTIwN1oXDTE1MDQwMTE2NTIwN1owFTETMBEGA1UEAxMKY2xpZW50dXNlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmvRe7Fw4zS4z+whmwFfzuma9R2GbG1kkZTzUkogDdR2zes6qnk84LxIwbyoLDL4HOBrm4ENdGMknYletrLppejRqXdyu4mkbJ1FH0zLAuGJzxN5vZluu0uQJvc1YmKVAbsAwDsKnkvURLn471YtBAtCJsEdUyYOESTOc2lQrrSMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBbJfRlYAyhGMkEUqKkb1epaxa6TfR5aAE4UPV5fbw5H7EYPJkC7i5Rg1uUqSC7ZI4+fNnMGU2Pbg9at4FLCyB9++KhQu0hWsE/AfwIIff+57aXBU3n3oJK+5RUaYaTmQWQnTc3nwui2eRgIG9yGiR1wkNntvYNVXmKsXP+h1e9Kg==2014-09-27T13:56:49.689Z2014-09-27T14:01:49.689Zo4dTpJf/6Meble547wkYIbB6M60=Oi7ejMXN+dPXHSmYUdAH+kxmB3XTscAbqp3TfMgSxegIBMdgb/Ce/HukLqZqgpClDZFX+iMKHSR8hCNdOM7X3v/gdL+zJbvSAgrRDt7+aPv+wdtSSjSzdhEVlWM5zIEsQ0QIAoz8P55hZjbRl4/4kJEf5Fu/ez909jslcApgp6M=WBhxAWuZAfryjQe7v04w87EMtvI=yvK+/PtNMYLa+S2qnMe3ZMkm0zE=aXQTtQLlupIbjWV3lhhrxcABAheoCT1EWDQPx6qIb1xwV6PgFedQIuX5+3a/I9cyrPEBcgTTooBu
      +Aot8R8nYdcOhwv8jgfuhmlsAZPSQFd0KE7S2u/SmAB2tC5RUSNAFKb2/bf2VXEzTQyk6b/Muom+
      z509bUL8/jLA1lUJpu8=cJ4gcuZwuuqskkjU9Hi9dUe2jjtVT+xKMypmnQf4CxkI/VXchAo7xgNoSrJLIPUlbdfBJ/TZ4c2dGkyV5L6DSBGSQz7B1BcvWulWQfrlvrLy1r0YtvGUtzmeON4TPLV0LfTeS+vz6p1bd3EP0mg5dJaVrnhedSpp/x3FWvTDbS/5OWfnJjR9plwW0ggbdYs+gfVh2NhBHcv7cXty+ABA2d2Cz7WChsYXgDKHm4S9PXwFiayH1uebAxEwSfUz0U8eC+eMCyZEFDorG9ZJgRhbtIBvvPxzyvOVg9BJISXNgNFFq25J0n79I0nhEOKht1wePJXAux+HR7PPeTZEygrqcGzbJO36vBBGhrDSIK/DBg8YIzGHmq8RGQ==
      --------------------------------------
      19:26:52,164 INFO  [STDOUT] com.kaustuv.poc.ws.service.greetings.GreetingsRequest@3a4c421b
      INFO: Outbound Message
      ---------------------------
      ID: 4
      Encoding: UTF-8
      Content-Type: text/xml
      Headers: {}
      Payload: MIIBoTCCAQqgAwIBAgIEUVm7NzANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDEwpjbGllbnR1c2VyMB4XDTEzMDQwMTE2NTIwN1oXDTE1MDQwMTE2NTIwN1owFTETMBEGA1UEAxMKY2xpZW50dXNlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmvRe7Fw4zS4z+whmwFfzuma9R2GbG1kkZTzUkogDdR2zes6qnk84LxIwbyoLDL4HOBrm4ENdGMknYletrLppejRqXdyu4mkbJ1FH0zLAuGJzxN5vZluu0uQJvc1YmKVAbsAwDsKnkvURLn471YtBAtCJsEdUyYOESTOc2lQrrSMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBbJfRlYAyhGMkEUqKkb1epaxa6TfR5aAE4UPV5fbw5H7EYPJkC7i5Rg1uUqSC7ZI4+fNnMGU2Pbg9at4FLCyB9++KhQu0hWsE/AfwIIff+57aXBU3n3oJK+5RUaYaTmQWQnTc3nwui2eRgIG9yGiR1wkNntvYNVXmKsXP+h1e9Kg==2014-09-27T13:56:52.253Z2014-09-27T14:01:52.253ZdXw6imyjRGr3un72RDm6T4MuYiaq2jHT3rtAWgQFWoeeCQXl05WMRi3sWVHyZFCmCZIqyjEh4yVbkNuS06KGRupQpP3PyC6f2YVFqxa5m2u0z+CZZRbeIxWuihwbL3mXDcJylOdgpfM8C1nJSXv6mbSKMk49NrSnL546FbUBRSc=SrBUKiCsZWWkUJ+g88BRlFbbv1Q=dI2z5gM35CEySeXSGZqTzb637dA=js1iy79LBQLszVLDR6q3keKlnXxTAb9DAe9OtXYC3Crm9tjd+jdox2F+JMacRa/frvgk+45ZmI2b1WTpn70ZdngTUlSSH393bmSFZKZnGiTDrzyPHNRYl/NPIESUsyW0kjQS6hK5RYt25SRHOGcBLrMz93VH6+DDSoqWGODKIO0=o4dTpJf/6Meble547wkYIbB6M60=HaP1FpvQM09q+e72nhytV2n/j/Nlz5wVk/wZdWWc234HPibx08f2/DGrvCuP9FdheC41XqgKHVa6/zLWw5I2jXoUIpxnVkyRyCxUHi3x7Cu7uQtRqvdGdhho0hrNI99d2fZ1QLDyXy3hRkiL3BD7d3Gmh1RTqLghDJOUkY6VqCvWT1xpVBV50Uu0fycub94br8fmKDZTOh8av78pF7DCfj9D+y3r4bvZwJXvVWbDbUVsKWFYcXQJJXwj1RahgxegvMnUfd5QRIPT0wympH41RnjccBMojBZ/NYLAwqMV0O32/IZZ2I7slkjYyoeQOp9OxgafQJnK3mM45ASaN+2VnSeY5W4d1AHgdi/PCfsTlJ6W7ry/gEXY2DXYh3ajyx9R
      --------------------------------------
      
      In above example console log we can see that entire message is encrypted now.
    Click here to download source code of above example

    Source Code


    • References:
    CXF documents

    European Union laws require you to give European Union visitors information about cookies used on your blog. In many cases, these laws also require you to obtain consent.

    As a courtesy, we have added a notice on your blog to explain Google's use of certain Blogger and Google cookies, including use of Google Analytics and AdSense cookies.

    No comments:

    Post a Comment