X

Geertjan's Blog

  • December 7, 2005

Message-Level Web Service Security and Demi Moore

Geertjan Wielenga
Product Manager
Let's say you want to send a super secret message somewhere, from within an application. A suitably secret message (because you're afraid of Ashton Kutcher or because you're worried that it might be uncool to think that Demi Moore is cool) might be: "Demi Moore is cool." How would you go about sending it? Let's try message-level web service security. And we'll use an IDE. Hmmm... which IDE shall we use. Let's try NetBeans IDE.

  1. Use wizards in NetBeans IDE 5.0 to create the 'Hi World' web service sample. (See the NetBeans IDE 5.0 Quick Start Guide for Web Services for details. By the way, when I wrote the first quick start doc for web services, in NetBeans IDE 4.1, I consciously decided to use 'Hi World' instead of 'Hello World'. Think about it—I saved you three key strokes for each instance of the phrase. Woohoo.)

    However, instead of implementing the web service client in a web application, I implemented it in a J2SE application. This is what the content looks like (everything in this file, apart from the text 'Demi Moore is cool' was created for me by the NetBeans IDE 5.0 wizards):

    package org.me.hi;
    public class Main {
    public Main() {
    }
    public static void main(String[] args) {
    try { // This code block invokes the HiWSSEIPort:sayHi operation on web service
    auctionsystem.client.HiWS hiWS = new auctionsystem.client.HiWS_Impl();
    auctionsystem.client.HiWSSEI hiWSSEIPort = hiWS.getHiWSSEIPort();
    System.out.println(hiWSSEIPort.sayHi("Demi Moore is cool"));
    } catch(javax.xml.rpc.ServiceException ex) {
    // TODO handle ServiceException
    } catch(java.rmi.RemoteException ex) {
    // TODO handle remote exception
    } catch(Exception ex) {
    // TODO handle custom exceptions here
    }
    }
    }

    Also, note that this is the content of my HiWSImpl file, which is on the service side, in the HiWS project:

    package org.me.hi;
    public class HiWSImpl implements HiWSSEI {
    public java.lang.String sayHi(String s) throws java.rmi.RemoteException {
    System.out.println("Received: " + s + "!");
    return "Received: " + s + "!";
    }
    }

  2. Download and install the Java Web Services Developer Pack (Java WSDP). Then, add the JAR files that you find in the JWSDP's xws-security\\lib folder to both the projects—HIWS and HIWSClient. (I haven't tried any of this for Tomcat, so I suggest you download the JWSDP that is not geared towards Tomcat and that you set the Sun Java System Application Server as the target server for both your projects.)

  3. On the client side, you need to do the following:

    • Override the HiWS-client-wscompile target. Do this by copying it from build-impl.xml into build.xml. Then add the security attribute to the wscompile element (the only addition to the standard target generated for you by the Web Service Client wizard is the security attribute, which is in bold below):

      <target name="HiWS-client-wscompile" depends="wscompile-init" unless="wscompile.client.HiWS.notrequired">
      <property name="config_target" location="${meta.inf.dir}/wsdl"/>
      <copy file="${meta.inf.dir}/wsdl/HiWS-config.xml" tofile="${build.generated.dir}/wsclient/wsdl/HiWS-config.xml" filtering="on">
      <filterset>
      <filter token="CONFIG_ABSOLUTE_PATH" value="${config_target}"/>
      </filterset>
      </copy>
      <wscompile security="deployment/xws-security/sendDemiMoore-security.xml"
      xPrintStackTrace="true"
      verbose="false"
      fork="true" keep="true" client="true"
      import="false"
      features="${wscompile.client.HiWS.features}"
      base="${build.classes.dir}"
      sourceBase="${build.generated.dir}/wsclient"
      classpath="${wscompile.classpath}:${javac.classpath}"
      mapping="${build.generated.dir}/wsclient/wsdl/HiWS-mapping.xml"
      httpproxy="${wscompile.client.HiWS.proxy}"
      config="${build.generated.dir}/wsclient/wsdl/HiWS-config.xml"/>
      </target>

      Notice that the security attribute points to sendDemiMoore-security.xml inside deployment/xws-security. So create those two folders and add an XML file with that name.

    • Define the client-side security configuration file. Here's the content:

      <xwss:JAXRPCSecurity xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
      <xwss:Service>
      <xwss:SecurityConfiguration dumpMessages="true">
      <xwss:Encrypt>
      <xwss:X509Token certificateAlias="secretsystem" id="sendDemi" />
      <xwss:EncryptionTarget type="uri" value="#sendDemi" />
      </xwss:Encrypt>
      </xwss:SecurityConfiguration>
      </xwss:Service>
      <xwss:SecurityEnvironmentHandler>
      org.me.security.ClientSecurityHandler
      </xwss:SecurityEnvironmentHandler>
      </xwss:JAXRPCSecurity>

      Notice that the configuration file points to org.me.security.ClientSecurityHandler. So create that class in that package structure.

    • Define the security handler. Click the
      ClientSecurityHandler.java link and paste the code into the class you defined in the org.me.security package.

      Notice that the file refers to the deployment/xws-security/client-security-env.properties file. So create that file, in the same package as where the client-side security configuration file (sendDemiMoore-security.xml) is found.

    • Define the security properties. Put the following key-value pairs in the properties file:

      truststore.url=deployment/xws-security/client-truststore.jks
      truststore.type=JKS
      truststore.password=secret

      Notice that the file refers to the client-truststore.jks file. We'll create that later. The client should now look as follows (without the JKS files, because you don't have them yet, they'll be created in step 5):

  4. On the service side, do the following:

    • Override the HiWS_wscompile target. Do this by copying it from build-impl.xml into build.xml. Then add the security attribute to the wscompile element (the only addition to the standard target generated for you by the Web Service wizard is the security attribute, which is in bold below):

      <target name="HiWS_wscompile" depends="wscompile-init">
      <wscompile security="deployment/xws-security/receiveMessage-security.xml"
      define="true"
      fork="true"
      keep="true"
      base="${build.generated.dir}/wsbinary"
      xPrintStackTrace="true"
      verbose="true"
      nonClassDir="${build.web.dir.real}/WEB-INF/wsdl"
      classpath="${wscompile.classpath}:${build.classes.dir.real}:${javac.classpath}"
      mapping="${build.web.dir.real}/WEB-INF/${HiWS.mapping}"
      config="${HiWS.config.name}"
      features="${wscompile.service.HiWS.features}"
      sourceBase="${build.generated.dir}/wsservice"/>
      </target>

      Notice that the security attribute points to receiveMessage-security.xml inside deployment/xws-security. So create those two folders and add an XML file with that name.

    • Define the service-side security configuration file. Here's the content:

      <xwss:JAXRPCSecurity xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
      <xwss:Service>
      <xwss:SecurityConfiguration dumpMessages="true">
      <xwss:Sign/>
      <xwss:RequireSignature/>
      </xwss:SecurityConfiguration>
      </xwss:Service>
      <xwss:SecurityEnvironmentHandler>
      org.me.security.HiServerSecurityHandler
      </xwss:SecurityEnvironmentHandler>
      </xwss:JAXRPCSecurity>

      In this file, you can see that a signature is required. Notice that the configuration file points to org.me.security.HiServerSecurityHandler. So create that class in that package structure.

    • Define the security handler. Click the
      HiServerSecurityHandler link and paste the code into the class you defined in the org.me.security package.

      Notice that the file refers to the deployment/xws-security/server-security-env.properties file. So create that file, in the same package as where the service-side security configuration file (receiveMessage-security.xml) is found.

    • Define the security properties. Put the following key-value pairs in the properties file:

      keystore.url=server-keystore.jks
      keystore.type=JKS
      keystore.password=secret

      Notice that the file refers to the server-keystore.jks file. We'll create that next.
      The client should now look as follows (without the JKS file and without the CERT file, because you don't have them yet, they'll be created in step 5, and also without ExtractHeadersMH.java which is referred to later in this blog entry):

  5. Use the Key and Certificate Management Tool. This tool is part of the JDK. Various ways can be adopted to use this tool. I like Ant, so I use the tool via Ant scripts from inside the IDE. (This means I can create keyboard shortcuts, toolbar buttons, and menu items for them. Woohoo!) So first, click the keytool-targets.xml link to get a text file containing the targets. Create an XML file and put the targets into it, exactly as they are in the text file. Put the XML file in the HiWS root directory (i.e., in the same folder as where build.xml is found in the web service project.) Then, here's a really cool thing (which I learnt from Petr Blaha recently). Paste this right below the XML declaration in your build.xml file:

    <!DOCTYPE project [
    <!ENTITY KeyToolTargets SYSTEM "keytool-targets.xml">
    ]>

    Now, put this entity reference on a line somewhere below the <import file="nbproject/build-impl.xml"/> line:

    &KeyToolTargets;

    Neat, right? This way, you keep your keytool targets in a tidy separate file. And, when you expand the build.xml file, you can right-click and run any of the keytool targets, just as if they were inside the build.xml file (but unfortunately, when you double-click a target, the cursor doesn't jump to the keytool-targets.xml file):

    Select each of the 5 keytool targets and run them one by one. You'll find files have been generated on the service-side, in the deployment/xws-security folder. Move the client-keystore.jks and client-truststore.jks to the same folder on the client side.

Now deploy the web service and the web service client. If you get the dreaded "SOAP must understand error" message when you run the client, see my blog entry from a few days ago. Basically, you need to create a message handler so that the web service can take the headers out of the client's message. The message handler is very easy to create, as explained in that blog entry.

Finally, all should be well and the web service receives the client's message and proves it by printing the received message in the log file, which is displayed in the Output window, as shown below:

The code and the concepts introduced in this blog entry come from the Sun Education course that I've been converting to NetBeans, called "Developing Secure Java Web Services". I highly recommend this course, because it covers a lot more than the basic steps described above—it goes into a lot of theoretical detail. On top of that, I'd recommend the Security chapter of the free Designing Web Services with the J2EE 1.4 Platform, as well as the chapters relating to Security in the J2EE 1.4 tutorial.

Join the discussion

Comments ( 1 )
  • guest Monday, June 25, 2007
    Hi,
    Can you please suggest how do I go about using XWS-Security and JWSDP on JBoss App server? Any suggestions/pointers will be most appreciated...
    Thanks
    Shivani
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.