2 Replies Latest reply on Sep 26, 2009 11:51 PM by Sean Thatcher

    Authenticating SOAP Requests with WSSE UsernameToken does no

    Sean Thatcher Newbie

      I have created a simple EJB3 project and exposed it as a web service (your typical Echo service). I'm using WSSE UsernameToken headers in the SOAP requests to send authentication info. I can successfully authenticate using a username and digesting the password (e.g. Base64 encode of the SHA-1 hash).

      However, when I encorporate the Created node and add to the digest I get an "Invalid User" exception. But, I can use just the hash of the password (without the created node) and authenticate successfully. According to the docs from the OASIS site the digest is computed as (taken from Web Services Security UsernameToken Profile):

      Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
      


      I'm currently not using a Nonce since the spec says that both the Nonce and the Created are optional.

      It's as if the server is ignoring the fact that the created node exists. I'm not fully confident that I have the jboss-wsse-server.xml file set up or even the security header. See below for the appropriate files. Any thoughts would be appreciated.

      EJB3 Service:
      package com.test;
      
      import javax.annotation.security.RolesAllowed;
      import javax.ejb.Stateless;
      import javax.jws.WebMethod;
      import javax.jws.WebParam;
      import javax.jws.WebResult;
      import javax.jws.WebService;
      
      import org.jboss.ws.annotation.EndpointConfig;
      
      @Stateless
      @WebService(endpointInterface = "com.test.IEchoSession", serviceName="EchoService", portName="EchoServiceSOAP")
      @EndpointConfig(configName = "Standard WSSecurity Endpoint")
      public class EchoSession implements IEchoSession {
      
       /**
       *
       */
       private static final long serialVersionUID = 1L;
      
       @Override
       @WebMethod(operationName="Echo")
       @WebResult(partName="EchoResponse")
       @RolesAllowed(value={"friend"})
       public String echo(@WebParam(partName="text") String text) {
       return "You said: " + text;
       }
      
      }
      


      jboss.xml (in the EJB3 META-INF folder):
      <?xml version="1.0" encoding="utf-8"?>
      <jboss>
       <security-domain>java:/jaas/MyRealm</security-domain>
       <webservices>
       <context-root>/echo</context-root>
       </webservices>
       <enterprise-beans>
       <session>
       <ejb-name>EchoSession</ejb-name>
       <port-component>
       <port-component-name>EchoSession</port-component-name>
       <port-component-uri>/EchoService</port-component-uri>
       <transport-guarantee>NONE</transport-guarantee>
       <secure-wsdl-access>false</secure-wsdl-access>
       </port-component>
       </session>
       </enterprise-beans>
      
      </jboss>


      jboss-wsse-server.xml (also in the META-INF):
      <jboss-ws-security xmlns="http://www.jboss.com/ws-security/config"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.jboss.com/ws-security/config
       http://www.jboss.com/ws-security/schema/jboss-ws-security_1_0.xsd">
       <config>
       <username/>
       <timestamp ttl="300"/>
       <timestamp-verification createdTolerance="100" warnCreated="false" expiresTolerance="100" warnExpires="false" />
       <authenticate>
       <usernameAuth/>
       </authenticate>
       </config>
       </jboss-ws-security>
      


      On the client side (flex component) the following header get's added to the request:

      private function addWSSEHeader(): SOAPHeader {
      
       var date : Date = new Date();
       _ds = getDateString(date);
       date.minutes += 3;
      
       var plain : String = SHA1.hashToBase64(password.text);
      
       _password = SHA1.hashToBase64(_ds + password.text);
      
       trace("plain: " + plain);
       trace("timestamp " + _password);
       var usernameTokenXML:XML =
       <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
       xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
       <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
       <wsu:Created>{_ds}</wsu:Created>
       <wsu:Expires>{getDateString(date)}</wsu:Expires>
       </wsu:Timestamp>
       <wsse:UsernameToken>
       <wsse:Username Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">{username.text}</wsse:Username>
       <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
       xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">{_password}</wsse:Password>
       <wsu:Created>{_ds}</wsu:Created>
       </wsse:UsernameToken>
       </wsse:Security>
      
       var wsse: Namespace = new Namespace("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
       var wsseSecurity: QName = new QName(wsse.uri, "Security");
       var header: SOAPHeader = new SOAPHeader(wsseSecurity, {"wsse":"Security"});
       header.content = usernameTokenXML;
      
       return header;
      }
      
      


      And here is an example of the SOAP message:
      <SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><SOAP-ENV:Header><wsse:Security xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
       <wsu:Timestamp>
       <wsu:Created>2009-09-25T13:07:29Z</wsu:Created>
       <wsu:Expires>2009-09-25T13:10:29Z</wsu:Expires>
       </wsu:Timestamp>
       <wsse:UsernameToken>
       <wsse:Username Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'>kermit</wsse:Username>
       <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest'>lKnNIypKvfb27kzvQynQtS+iNUc=</wsse:Password>
       <wsu:Created>2009-09-25T13:07:29Z</wsu:Created>
       </wsse:UsernameToken>
       </wsse:Security></SOAP-ENV:Header><SOAP-ENV:Body>
       <tns:echo xmlns:tns='http://test.com/'>
       <arg0>test</arg0>
       </tns:echo>
       </SOAP-ENV:Body></SOAP-ENV:Envelope>
      


        • 1. Re: Authenticating SOAP Requests with WSSE UsernameToken doe
          Sean Thatcher Newbie

          I did a bit more investigation on this and found the crux of the issue but I'm not sure how to solve it. In the login configuration for the realm you are supposed to use a UsernameTokenCallback object to update the digest based on the existence of a nonce or created. However, on debugging, these values are always null. And the init function sends a map with both keys (nonce and created) set to null. So the question is what other configuration do I need to tell JBoss to pull out these header nodes and send to the callback handler.

          The specific login config is as follows:

           <application-policy name="MyRealm">
           <authentication>
           <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
           flag="required">
           <!--
           <module-option name="usersProperties">props/myrealm-users.properties</module-option>
           <module-option name="rolesProperties">props/myrealm-roles.properties</module-option> -->
           <module-option name = "dsJndiName">java:/WSExampleDS</module-option>
           <module-option name = "principalsQuery">SELECT password FROM users WHERE username=?</module-option>
           <module-option name = "rolesQuery">SELECT r.role, 'Roles' FROM roles r join users u on u.id = r.userid WHERE u.username=?</module-option>
           <module-option name="hashAlgorithm">SHA</module-option>
           <module-option name="hashEncoding">BASE64</module-option>
           <module-option name="hashUserPassword">false</module-option>
           <module-option name="hashStorePassword">true</module-option>
          
           <module-option name="unauthenticatedIdentity">anonymous</module-option>
           <module-option name="storeDigestCallback">org.jboss.ws.extensions.security.auth.callback.UsernameTokenCallback</module-option>
          
           </login-module>
           </authentication>
           </application-policy>