Announcement Announcement Module
Collapse
No announcement yet.
WS-Security Support for WebService Clients? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • WS-Security Support for WebService Clients?

    The Spring-WS manual talks about security only in the context of the service producer and never in the context of the service client.
    Shouldn't there be an equivalent to the XwsSecurityInterceptor (= EndpointInterceptor) on the client which lets me add authentication information (UsernameToken or a BinarySecurityToken) to the outgoing SOAP message and sign and encrypt it? Do I have to do this manually?

  • #2
    Here is an article on security for a Spring-WS client. I have it working except my client is having problems with the return message. I get a "premature end of file" error I have yet to resolve. I think Spring-WS is supposed to integrate security better on the client.

    http://www.jroller.com/0xcafebabe/en...s_client_with1

    Comment


    • #3
      Help each other out

      I have developed the code to sign calls from the client side using x509 certificates and also added the server side code but seem to get a strange error. Not sure if it is the policy file or bad certificates. I developed a call back bean to sign the message using my keystore. Here is the context configuration:

      <bean id="signMessageCallback" class="com.truewind.ws.SignMessageCallback">
      <constructor-arg value="securityPolicy.xml" />
      <constructor-arg>
      <bean
      class="org.springframework.ws.soap.security.xwss.c allback.KeyStoreCallbackHandler">
      <property name="keyStore">
      <bean class="org.springframework.ws.soap.security.suppor t.KeyStoreFactoryBean">
      <property name="location" ref="signatureKeyStoreFile"/>
      <property name="password" value="@{signature.keystore.password}"/>
      </bean>
      </property>
      <property name="defaultAlias" value="@{default.alias}"/>
      <property name="privateKeyPassword" value="@{privateKey.password}"/>
      </bean>
      </constructor-arg>
      <property name="soapAction" value="@{soap.action}" />
      </bean>


      On the server side I have configured the XwsSecurityInterceptor and a KeyStoreCallBackHandler for checking the x509 certificate. I have added the following configuration to the spring-ws-servlet.xml:

      <bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.c allback.KeyStoreCallbackHandler">
      <property name="trustStore" ref="trustStore"/>
      </bean>

      <bean id="trustStore" class="org.springframework.ws.soap.security.suppor t.KeyStoreFactoryBean">
      <property name="location" value="classpath:trust_store.jks"/>
      <property name="password" value="test"/>
      </bean>

      <bean id="wsSecurityInterceptor"
      class="org.springframework.ws.soap.security.xwss.X wsSecurityInterceptor">
      <property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
      <property name="callbackHandler" ref="keyStoreHandler"/>
      <property name="validateRequest" value="false"></property>
      </bean>


      Now I am getting a Security Verification Error. I checked to make sure the certificate I am signing with is indeed in the truststore and vice versa. I have been trying for days to figure this out but have been running into a brick wall.

      I will exchange my design with you if you can help me diagnose this issue. Let me know.

      Thanks,
      Hisham

      Comment


      • #4
        signing with a X509 certificate

        Yes, that article is helpful, since there's nothing in the Spring-WS manual about it.

        I too would like to sign my messages with a X509 certificate instead of using a username/password. I think I have my securityPolicy.xml file correct, but I can't figure out how to tell my app what keystore to use since there's no client-side way to specify that in Spring like there is on the server side with KeyStoreCallbackHandler.

        I developed a call back bean to sign the message using my keystore.
        ghazouli: I would be interested to see how you're managing to use KeyStoreCallbackHandler on the client.

        I have generated a keystore in my classpath using keytool, but when I run my app I get exceptions that make me think my app isn't reading the correct keystore and is defaulting to somewhere else. Therefore it can't find a certificate with the alias "myCertAlias" as specified in my securityPolicy.xml.

        Here's my securityPolicy.xml:

        Code:
        <xwss:SecurityConfiguration dumpMessages="true"
           xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
           <xwss:Sign>
              <xwss:X509Token certificateAlias="myCertAlias" />
              <xwss:CanonicalizationMethod
                 algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
              <xwss:SignatureMethod
                 algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
                 
              
              <!-- Sign the message body. -->
              <xwss:SignatureTarget type="xpath" value="./SOAP-ENV:Envelope/SOAP-ENV:Body">
                 <xwss:DigestMethod
                    algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                 <xwss:Transform
                    algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
                    <xwss:AlgorithmParameter name="XPATH"
                       value="./SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature[1]/ds:KeyInfo/wsse:SecurityTokenReference" />
                 </xwss:Transform>
                 <xwss:Transform
                    algorithm="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform">
                    <xwss:AlgorithmParameter name="CanonicalizationMethod"
                       value="http://www.w3.org/2001/10/xml-exc-c14n#" />
                 </xwss:Transform>
              </xwss:SignatureTarget>
              
           </xwss:Sign>
        </xwss:SecurityConfiguration>
        Here's the exception:

        Code:
        Feb 5, 2008 11:25:38 AM com.sun.xml.wss.impl.misc.DefaultSecurityEnvironmentImpl getAliasPrivKeyCertRequest
        SEVERE: WSS0216: Callback Handler failed for SignatureKeyCallback.AliasPrivKeyCertRequest
        Feb 5, 2008 11:25:38 AM com.sun.xml.wss.impl.misc.DefaultSecurityEnvironmentImpl getAliasPrivKeyCertRequest
        SEVERE: WSS0217: Exception in Callback Handler handle()
        java.lang.NullPointerException
        	at com.sun.xml.wss.impl.misc.DefaultSecurityEnvironmentImpl.getAliasPrivKeyCertRequest(DefaultSecurityEnvironmentImpl.java:205)
        	at com.sun.xml.wss.impl.filter.SignatureFilter.process(SignatureFilter.java:146)
        	at com.sun.xml.wss.impl.HarnessUtil.processWSSPolicy(HarnessUtil.java:64)
        	at com.sun.xml.wss.impl.HarnessUtil.processDeep(HarnessUtil.java:218)
        	at com.sun.xml.wss.impl.SecurityAnnotator.processMessagePolicy(SecurityAnnotator.java:143)
        	at com.sun.xml.wss.impl.SecurityAnnotator.secureMessage(SecurityAnnotator.java:118)
        	at com.sun.xml.wss.impl.misc.XWSSProcessor2_0Impl.secureOutboundMessage(XWSSProcessor2_0Impl.java:77)
        yada, yada, yada...
        My client code is almost exactly what's in cafebabe's article.

        There's nothing of note in my application context xml because there's no client-side security support. It's just a basic WebServiceTemplate and some XmlBeans config that has nothing to do with the security.

        Any help or suggestions would be greatly appreciated. I'm going to try putting that keystore in my home directory instead, in case that's the default location it's looking in. Then I might get adventurous and try out Spring-WS 1.5 M1 which is supposed to have client security support baked in.

        Comment


        • #5
          Have a look

          I am attaching my code for you reference. There are two classes:

          1. SendResults - extends WebServiceGatewaySupport so that you can make a callback.

          2. SignMessageCallback - this class implements WebServiceMessageCallback so that I can secure the soap message.

          I am also including the applicationContext.xml and securityPolicy.xml.

          I see that the message is getting signed or at least I think it is but my listener can't seem to validate the security token.

          Can you explain your securityPolicy? I am not sure if I need to declare the x509 certificate or not? I assume that it will pick up the default one in the keystore.

          Comment


          • #6
            Thanks for your answers to my original post.
            I posted my question before I tried to add security to my test service. I will do so with the help of your suggestions, but it will take me a while before I can tell you my results.
            I did look at the 1.5 M1 release but couldn't find the client security support, and the manual seems unchanged, including the typos.

            Ursus

            Comment


            • #7
              See the roadmap for 1.5 M2.

              Client-side ws-security support is fixed for this release.

              Comment


              • #8
                ghazouli:
                Your code makes sense, now that I finally got around to looking at it. I had not realized the KeyStoreCallbackHandler could be passed to XWSSProcessorFactory.createProcessorForSecurityCon figuration() as the CallbackHandler parameter, but now I see it does implement that interface.

                My securityConfig.xml is based on an example file "signv2.xml" from the XWS-Security samples.

                I'm still having problems, though. Here's my relevant code, the exception and the description of the problem.

                Code:
                import LoginRequestDocument;
                import LoginRequestType;
                
                import java.io.FileNotFoundException;
                import java.io.IOException;
                
                import javax.xml.soap.SOAPMessage;
                
                import org.apache.xmlbeans.XmlObject;
                import org.apache.xmlbeans.XmlOptions;
                import org.springframework.core.io.Resource;
                import org.springframework.ws.WebServiceMessage;
                import org.springframework.ws.client.core.WebServiceMessageCallback;
                import org.springframework.ws.client.core.WebServiceTemplate;
                import org.springframework.ws.soap.saaj.SaajSoapMessage;
                import org.springframework.ws.soap.security.xwss.XwsSecuritySecurementException;
                import org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler;
                import org.springframework.xml.transform.StringResult;
                import org.springframework.xml.transform.StringSource;
                
                import com.sun.xml.wss.ProcessingContext;
                import com.sun.xml.wss.XWSSProcessor;
                import com.sun.xml.wss.XWSSProcessorFactory;
                import com.sun.xml.wss.XWSSecurityException;
                
                public class TestClient {
                
                   private WebServiceTemplate webServiceTemplate;
                   
                   private XmlOptions xmlOptions;       // Injected by application context.
                   private XWSSProcessor cprocessor;
                   private ProcessingContext context;
                   
                   public TestClient(Resource xwssConfig, KeyStoreCallbackHandler keystoreHandler) throws XWSSecurityException, IOException {
                      if (!xwssConfig.exists()) {
                         throw new FileNotFoundException();
                      }
                
                      XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance();
                      cprocessor  = factory.createProcessorForSecurityConfiguration(xwssConfig.getInputStream(), keystoreHandler);
                      context = new ProcessingContext();
                
                   }
                   
                
                   public void login() {
                      // XMLBeans objects.
                      LoginRequestDocument loginDoc = LoginRequestDocument.Factory.newInstance();
                      LoginRequestType loginRequest = loginDoc.addNewLoginRequest();
                      
                      sendAndReceive("http://localhost:8080/EchoServlet/", loginDoc, true);
                   }
                   
                   private void sendAndReceive(  
                         final String requestUri, 
                         final XmlObject bodyDocument,
                         final boolean doSign) {
                      
                      // Create the SOAP response message stream.
                      StringResult result = new StringResult();
                
                      // Create the SOAP body source; i.e. the service request.
                      StringSource source = new StringSource(bodyDocument.xmlText(xmlOptions));
                      
                      webServiceTemplate.sendSourceAndReceiveToResult(requestUri, source, 
                         new WebServiceMessageCallback() {
                
                            public void doWithMessage(WebServiceMessage message)
                                  throws IOException {
                               
                               SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
                               SOAPMessage saajMessage = saajSoapMessage.getSaajMessage();
                               try {
                                  context.setSOAPMessage(saajMessage);
                                  SOAPMessage securedMessage = cprocessor.secureOutboundMessage(context);
                                  saajSoapMessage.setSaajMessage(securedMessage);
                               } catch (XWSSecurityException e) {
                                  throw new XwsSecuritySecurementException(e.getMessage());
                               }  
                            }
                         },
                         result
                      );
                      
                      System.out.println(result.getWriter().toString());
                   }
                   
                   public void setWebServiceTemplate(WebServiceTemplate webServiceTemplate) {
                      this.webServiceTemplate = webServiceTemplate;
                   }
                   
                   public XmlOptions getXmlOptions() {
                      return xmlOptions;
                   }
                
                   public void setXmlOptions(XmlOptions xmlOptions) {
                      this.xmlOptions = xmlOptions;
                   }
                   
                }
                and my app context:
                Code:
                <?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:lang="http://www.springframework.org/schema/lang"
                   xmlns:tx="http://www.springframework.org/schema/tx"
                   xmlns:util="http://www.springframework.org/schema/util"
                   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                      http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd
                      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.1.xsd
                      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
                
                   <bean id="webServiceClient"
                      class="TestClient">
                
                      <constructor-arg>
                         <bean class="org.springframework.core.io.ClassPathResource">
                            <constructor-arg value="securityPolicy.xml" />
                         </bean>
                      </constructor-arg>
                      <constructor-arg ref="keyStoreHandler" />
                      
                      <property name="webServiceTemplate" ref="webServiceTemplate" />
                      <property name="xmlOptions" ref="xmlOptions" />
                   </bean>
                
                   <!-- Configuration options for XMLBeans unmarshalling. -->
                   <bean id="xmlOptions"
                      class="org.springframework.oxm.xmlbeans.XmlOptionsFactoryBean">
                      <property name="options">
                         <map>
                            <entry key="SAVE_SUGGESTED_PREFIXES">
                               <map>
                                  <entry key="http://www.w3.org/2001/XMLSchema"
                                     value="xs" />
                                  <entry key="http://www.w3.org/2000/09/xmldsig#"
                                     value="ds" />
                                  <entry
                                     key="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                                     value="wsse" />
                               </map>
                            </entry>
                         </map>
                      </property>
                   </bean>
                
                   <bean id="webServiceTemplate"
                      class="org.springframework.ws.client.core.WebServiceTemplate">
                      <property name="messageFactory" ref="soapMessageFactory" />
                      <property name="defaultUri"
                         value="http://localhost:8080/EchoServlet/" />
                      <property name="messageSender" ref="commonsMessageSender" />
                   </bean>
                
                   <!-- Handles the underlying HTTP transport. -->
                   <bean id="commonsMessageSender"
                      class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
                   </bean>
                
                   <!-- Used to read and write SOAP messages. -->
                   <bean id="soapMessageFactory"
                      class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
                      <!--     <property name="payloadCaching" value="true" /> -->
                   </bean>
                
                   <bean id="keyStoreHandler"
                      class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
                      <property name="keyStore" ref="keyStore" />
                      <property name="defaultAlias" value="bobo" />
                      <property name="privateKeyPassword" value="password" />
                   </bean>
                   <bean id="keyStore"
                      class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
                      <property name="location" value="classpath:mystore.jks" />
                      <property name="password" value="password" />
                   </bean>
                
                </beans>
                I generated my keystore like this:

                Code:
                keytool -genkey -alias bobo -keypass password -keystore .\mystore.jks
                So I'm just trying to test with a self-signed certificate at the moment. I'll try to use a real one later.

                When I run an application to load a TestClient from the app context and call login(), I get an exception saying it can't find the X509 certificate. I'm so new to all this, I don't understand what's going on.
                Code:
                Feb 7, 2008 10:41:07 AM com.sun.xml.wss.impl.filter.SignatureFilter process
                SEVERE: WSS1417: Error while processing signature No X509Certificate was provided
                Exception in thread "main" org.springframework.ws.soap.security.xwss.XwsSecuritySecurementException: com.sun.xml.wss.XWSSecurityException: com.sun.xml.wss.XWSSecurityException: No X509Certificate was provided
                	at TestClient$1.doWithMessage(TestClient.java:102)
                	at org.springframework.ws.client.core.WebServiceTemplate$4.doWithMessage(WebServiceTemplate.java:363)
                	at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:404)
                	at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:359)
                	at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:305)
                	at TestClient.sendAndReceive(TestClient.java:89)
                	at TestClient.login(TestClient.java:67)
                	at Application2.main(Application2.java:12)
                If it couldn't find the keystore, I figure I'd get a FileNotFoundException. This sounds like it can't find the cert in the keystore, or it's not triggering the KeyStoreCallbackHandler at all.

                Edit: I just wrapped the KeyStoreCallbackHandler in my own simple CallbackHandler implementation with some console logging added in the handle() method. KeyStoreCallbackHandler.handle() is being called.
                Last edited by barsimp47; Feb 7th, 2008, 12:45 PM.

                Comment


                • #9
                  Moved to Axis client

                  I was having so much trouble and running out of time that I moved my code to Axis. It wasn't as bad as I thought it would be. Of course, I modified a sample application already in place.

                  The KeystoreBean does not throw an exception if it doesn't find the file. It will simply create a blank keystore. I frankly don't like that because it means things fail silently and you end up having to trace the code to figure out what went wrong.

                  The whole certificate thing is a pain. Sometimes I think I get it and other times I scratch my head. I know there are could some issues with the whole certificate chain, which includes the trusted authority that signed it, etc..

                  It sounds like maybe you don't have the .jks file in the classpath. Make sure it is in the classpath prior to running the application. You can copy/move it to top level of you package (com).

                  One other thing to consider is using 1.5-m2 which uses WSS4J. WSS4J is Apache's security implementation and compatible with Axis. I don't think the JWSDP version is compatible for some reason, which is why I moved to Axis. Good luck with that.

                  I am still trying to debug the security issue on the server side now. I think when I refactored my code I screwed something up. I will have to back track to an older version to see what went wrong.

                  Hisham

                  Comment


                  • #10
                    Hisham,
                    Do you mean you switched your SoapMessageFactory implementation from SAAJ to Axiom or that you stopped using Spring-WS for your client altogether?

                    Comment


                    • #11
                      I stopped using spring-ws for the client. I did it because that is the technology that my client knows and did not want to introduce another technology stack.

                      Comment


                      • #12
                        Note that the current milestone (1.5 m2) contains the client-side interception model, and also a client-side WS-security interceptor. There is no documentation for this yet (that comes in the next release: 1.5 RC1), but the airline sample includes a spring-ws client which uses this model. You can also check out the various unit tests.

                        Comment


                        • #13
                          Hi Hisham,

                          Are you able to resolve the Security Verification error, I'm trying to implement XWSS (X.509) security to a Web Service on Weblogic9.2, I'm able to send the signature in the request but on the server side I'm getting a Verification error, Kindly help!



                          Originally posted by ghazouli View Post
                          I have developed the code to sign calls from the client side using x509 certificates and also added the server side code but seem to get a strange error. Not sure if it is the policy file or bad certificates. I developed a call back bean to sign the message using my keystore. Here is the context configuration:

                          <bean id="signMessageCallback" class="com.truewind.ws.SignMessageCallback">
                          <constructor-arg value="securityPolicy.xml" />
                          <constructor-arg>
                          <bean
                          class="org.springframework.ws.soap.security.xwss.c allback.KeyStoreCallbackHandler">
                          <property name="keyStore">
                          <bean class="org.springframework.ws.soap.security.suppor t.KeyStoreFactoryBean">
                          <property name="location" ref="signatureKeyStoreFile"/>
                          <property name="password" value="@{signature.keystore.password}"/>
                          </bean>
                          </property>
                          <property name="defaultAlias" value="@{default.alias}"/>
                          <property name="privateKeyPassword" value="@{privateKey.password}"/>
                          </bean>
                          </constructor-arg>
                          <property name="soapAction" value="@{soap.action}" />
                          </bean>


                          On the server side I have configured the XwsSecurityInterceptor and a KeyStoreCallBackHandler for checking the x509 certificate. I have added the following configuration to the spring-ws-servlet.xml:

                          <bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.c allback.KeyStoreCallbackHandler">
                          <property name="trustStore" ref="trustStore"/>
                          </bean>

                          <bean id="trustStore" class="org.springframework.ws.soap.security.suppor t.KeyStoreFactoryBean">
                          <property name="location" value="classpath:trust_store.jks"/>
                          <property name="password" value="test"/>
                          </bean>

                          <bean id="wsSecurityInterceptor"
                          class="org.springframework.ws.soap.security.xwss.X wsSecurityInterceptor">
                          <property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
                          <property name="callbackHandler" ref="keyStoreHandler"/>
                          <property name="validateRequest" value="false"></property>
                          </bean>


                          Now I am getting a Security Verification Error. I checked to make sure the certificate I am signing with is indeed in the truststore and vice versa. I have been trying for days to figure this out but have been running into a brick wall.

                          I will exchange my design with you if you can help me diagnose this issue. Let me know.

                          Thanks,
                          Hisham

                          Comment


                          • #14
                            No I never did resolve the issue. Although I think the new version of Spring WS may be more compatible.

                            Perhaps if you post the details of the client and server code than me or someone else may be able to see the issue.

                            Comment


                            • #15
                              I am successfully signing messages using the new ClientInterceptor features in Spring-WS 1.5.x, but I'm using WSS4J and Axiom under the hood, not XWSS or SAAJ. (The problems I was having with Axiom before were apparently resolved in the new version as well.)

                              Wss4jSecurityInterceptor is working like a charm with a pkcs12 keystore type.

                              Comment

                              Working...
                              X