HomeDigital EditionSys-Con RadioSearch Web Services Cd
B2B Beginning WS Business Process Management Case Studies Content Management Distributing Computing e-Business Electronic Data Interchange Enterprise Industry Insight Integration Interviews Java & Web Services .NET Portal Product Reviews Scalability & Performance Security SOAP Source Code UDDI Wireless WS Standards WS Tips & Techniques WSDL WS Editorials XML

Source Code for this article

XML signatures apply digital signatures to XML documents. Digital signatures let parties that exchange data ensure the identity of the sender and the integrity of the data. This last item is a benefit that physical signatures can't provide.

Digital signatures don't have the legal status that physical signatures have, at least not yet. Over the last few years this has been changing, as the federal government and many state governments have moved to accept digital signatures as legally binding for some applications, where they identify the sender and provide nonrepudiation (the signer can't deny ever having signed). Since Web services that use SOAP exchange XML documents, they can include XML signatures. Web service developers who produce Web services with XML signatures will be able to realize the benefits of those signatures, and may be able to use them in place of physical signatures where the legal community has agreed they are binding. Putting XML signatures into a Web service, however, is not trivial. In this article I'll provide some background on XML signatures, and then explore how you can incorporate them into your Web services. I'll develop an example Web service, written in Java for Apache Axis, that uses an XML signature, to illustrate how it can be done.

XML Signatures
XML signatures are the work of the W3C XML Signature Working Group (www.w3.org/Signature), which produced "W3C Recommendation on XML Signature Syntax and Processing" (February 12, 2002). The recommendation gives an XML schema for the signature syntax and also refers to recommendations on canonicalization, key type, transformation algorithm, digest algorithm, and identity certification by authorities. The recommendation specifies what may go into each part of an XML signature.

IBM and Microsoft have been leading an effort to develop Web service security, including the use of XML signatures in Web services. They produced the WS-Security specification (www-106.ibm.com/
developerworks/library/ws-secure
) along with VeriSign, Inc. They recently submitted this specification to OASIS, which created a technical committee dedicated to that proposal. This specification defines how a SOAP header entry can be used to carry an XML signature referring to information elsewhere in the same SOAP envelope (the basis for my code example). Placing the signature in a SOAP header makes it easier for software to locate.

XML signatures are the application of digital signatures to XML - you can digitally sign part or all of an XML document. To create a digital signature, you calculate a checksum (digest) of the XML to be signed, and encrypt the digest using the private key of a public/private key pair (PKI). You send the encrypted digest along with the data to be signed, and also attach the public key and a certificate issued by an authority to identify the owner of the public key (the latter two items don't have to be passed if the receiver already has them). The receiver decrypts the digest and checksums it against the received information. If it matches, the data's integrity is assured and the identity of the sender is tied to the public key. By using the certificate, the receiver can be assured that the public key belongs to the desired sender, and not someone else who may have hijacked the digitally signed data and substituted their own keys, digest, and signature.

The XML signatures recommendation doesn't concern itself with the transport used to get the XML document to its destination. XML signatures could be applied to XML documents that are e-mailed or carried on a floppy disk. But, of course, sending information across the Web opens it up to attack, making security more important, and that is exactly the case for Web services.

There has been recent legislation on both the state and federal levels covering the use of digital signatures. (For a good survey of this legislation, see the McBride Baker & Coles Web site legislative tables [www.mbc.com/ecommerce/ecommerce.asp].) For example, the use of digital signatures has been addressed in federal legislation such as the "Electronic Signatures in Global and National Commerce Act" (E-SIGN), and has been used on a trial basis for IRS tax returns.

Using XML Signatures in Your Web Service
First, let's assume you are working on an application where you need the benefits of XML signatures. In my example, I'm developing an insurance claim system wherein agents can submit claims containing accident reports. Each agent is assigned a private/public key pair, which is installed on his or her computer in a keystore (a database of public and private keys and certificates). Note that you can use keytool (from Sun Microsystems) to create keystores for your own work. The agent signs each accident report using the private key (by telling the application where the keystore is and providing the proper identifier and passwords). The agent submits the claim containing the signed accident report(s) via a Web service to the central office.

At the office, each XML signature is verified, and if the identity of the submitter is accepted, the claim is processed. By verifying the XML signature, the central office has not only verified the identity of the sender, but also that the information in the accident report has not been altered after it was signed.

Now let's consider how to design and implement the Web service. For my example I will be using Apache Axis (available at http://XML.apache.org/axis). I'll be writing the code for my Web service in Java, and I'll develop a message-style Web service. Many Web service developers start with Java code and generate the portions of the Web service that talk XML from it automatically, shielding themselves from dealing with XML. For this insurance claim Web service though, where an XML signature needs to be applied to the XML that represents my data, I will produce my own XML.

I choose to design my service interface first so that I'll know what the XML should look like. I do this by authoring a WSDL file that describes my service. Its data definitions are done in an XML schema. Since the WSDL file should describe the messages that pass across the wire, my data definitions show the XML signature along with the data it signs. Listing 1 shows an excerpt from the wsdl file, the first portion from the XML schema that defines my data. The signature element is carried in the SOAP header, and I define it as a reference to the XML signature Signature element. The AccidentReport element is the data to be signed; it's in the SOAP body. The top-level element in the SOAP body, Claim, contains an AccidentReport element whose type is AccidentReportType.

The second portion of Listing 1 shows the WSDL message definition for the request message: the first part (name = "Claim") uses the Claim type, and goes into the SOAP body part of the SOAP envelope. The second part (name = "Signature") refers to the W3C Signature element, and goes into the SOAP header part of the SOAP envelope. When I define the WSDL describing my Web service operation, I use document/literal. I don't want SOAP encoding applied to the data being signed, and I don't plan to do any such encoding myself.

Now I can implement the Web service. I use the Apache XML security packages (available at http://XML.apache.org/security), which you can use from your Java classes, and which operate on DOM objects (which represent XML data). Therefore, I need to operate on the messages in my Web service at the DOM level.

To do this, I write a message-style Web service; I don't use Apache's ability to automatically serialize/deserialize Java classes. I use the Axis sample LogHandler.Java (which you find in the Axis security samples), which intercepts incoming messages. It receives a MessageContext, from which it gets the Signature element out of the SOAP header and verifies it against the SOAP body. If the verification succeeds, the handler passes the message (without the signature) on to the Web service server itself. The server proceeds with processing the claim (in my example, I used a simple server that echoes back the elements it received). If the signature verification fails, the log handler raises an exception, which causes a SOAP fault to be returned.

On the client side, I also work at the DOM and message level. My code translates the data to be signed into DOM (along with the data that will not be signed), constructs the XML signature, and puts both into their proper place in a SOAP envelope (the signature in the SOAP header, the data in the SOAP body). Finally, it sends the message (via the invoke method). Listing 2 shows the class that represents the data to be signed; the class includes a method asDOM() that returns a DOM representation of itself. Listing 3 shows the main body of the client, which populates the Claim to be submitted; creates a SOAP envelope; causes the correct parts of it to be signed; and calls call.invo ke(), which sends the outgoing message. Listing 4 shows excerpts from the class that signs the AccidentReport portion of the SOAP envelope. For more details on how to do the signing, canonicalization, serializing, and deserializing, see the Apache Axis security samples.

Of course, my code could just as easily have signed the entire SOAP body, instead of just signing the AccidentReport. This would have a different effect - it would ensure that the entire message has been delivered correctly, and not necessarily that the correct party vouches for the AccidentReport.

Conclusion
As you can see from my example, it can be a bit of a struggle to use XML signatures in a Web service. Their relationship to the XML data they sign makes XML signatures hard to use in a Web service tool that seeks to hide XML from its user. It would take special mechanisms to express the relationship between signed data and signature at the programming language level, so that a Web service tool could handle the details for you. As my example shows, in Apache Axis it is easiest to get the XML signature out of the SOAP header of the message (in a log handler) before the main part of the Web service begins. So the developer must write some code that can work in a separate handler. This makes the developer's job a bit harder, since he or she must split the server logic into two parts, and possibly make them communicate (so that the handler can pass the service proper any signature information it needs).

Author Bio
Mark Young is vice president of Kamiak Corporation, the publisher of Omniopera WS, a WSDL and XML Schema editor. He is an expert in XML Schema and WSDL, and has written object-oriented libraries to model them. Mark has 20 years of software development experience, has been a software team leader for many years, and has developed software for applications from telephony to sales automation. mark@kamiak.com

Designing Web Services with XML Signatures, by Mark Young
WSJ Vol 02 Issue 10 - pg.12

	



Listing 1

<xsd:element name=""Claim"" type=""tns:ClaimType""/>
<xsd:complexType name=""ClaimType"">
<xsd:sequence>
 <xsd:element name=""AccidentReport"">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name=""AccidentDescription"" 
      type=""xsd:string""/>
    <xsd:element name=""AccidentDate"" 
      type=""xsd:date""/>
   </xsd:sequence>
   <xsd:attribute name=""id"" type=""xsd:ID"">
   </xsd:attribute>
  </xsd:complexType>
 </xsd:element>
 <xsd:element name=""Submitter"">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name=""Name"">
     <xsd:complexType>
      <xsd:sequence>
       <xsd:element name=""Last"" 
         type=""xsd:string""/>
       <xsd:element name=""First"" 
         type=""xsd:string""/>
       <xsd:element name=""MiddleInitial"" 
         type=""xsd:string""/>
      </xsd:sequence>
     </xsd:complexType>
    </xsd:element>
    <xsd:element name=""Company"" 
      type=""xsd:string""/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
</xsd:sequence>
</xsd:complexType>

...

<wsdl:message name=""SubmitClaimSoapIn"">
  <wsdl:part name=""Claim"" element=""ns:Claim""/>
  <wsdl:part name=""Signature"" 
    element=""soap-sec:Signature""/>
</wsdl:message>


...

<wsdl:binding name=""ClaimServiceSOAPBinding"" 
   type=""ns:ClaimServiceSOAP"">
  <soap:binding style=""document"" >
  <wsdl:operation name=""SubmitClaim"">
   <soap:operation style=""document"">
   <wsdl:input name=""SubmitClaimInput"">
    <soap:body use=""literal"" parts=""Claim""/>
    <soap:header use=""literal"" part=""Signature""
      message=""ns:SubmitClaimSoapIn""/>
   </wsdl:input>
   ...
  </wsdl:operation>
 </wsdl:binding>

Listing 2

package com.myOrg.ClaimService.Client;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.apache.axis.message.MessageElement;
import Java.io.FileOutputStream;
import Java.io.Writer;

public class AccidentReport {
  static private Java.lang.String
  nsString = ""http://www.claimsRUs.com/claimDef"";
  private String mAccidentDescription;
  private String mAccidentDate;

  public AccidentReport() {
  }

  public void setDescription(String aString) {
    mAccidentDescription = aString;
  }

  public String getDescription() {
    return mAccidentDescription;
  }

  public void setDate(String aString) {
    mAccidentDate = aString;
  }

  public String getDate() {
    return mAccidentDate;
  }

  public Element asDOM(Document aDoc) {

    Element accidentReportEl =
      aDoc.createElementNS(nsString,
      ""AccidentReport"");
    // this is how the signature will refer
    // to this element
    accidentReportEl.setAttribute(""Id"", ""Ar1"");

    Element descriptionEl =
        aDoc.createElementNS(nsString,
        ""AccidentDescription"");
    Node descriptionTxt =
       aDoc.createTextNode(mAccidentDescription);
    descriptionEl.appendChild(descriptionTxt);
    accidentReportEl.appendChild(descriptionEl);


    Element dateEl = aDoc.createElementNS(
        nsString, ""AccidentDate"");
    Node dateTxt =
       aDoc.createTextNode(mAccidentDate);
    dateEl.appendChild(dateTxt);
    accidentReportEl.appendChild(dateEl);



    return accidentReportEl;
  }

  static public AccidentReport populate() {
    // stuff with some test data
    AccidentReport aReport = new AccidentReport();
    aReport.setDescription(""Fender bender."");
    aReport.setDate(""1-2-2002"");
    return aReport;
  }

}

Listing 3

package com.myOrg.ClaimService.Client;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.message.SOAPBodyElement;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.utils.Options;
import org.apache.axis.utils.XMLUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Document;

public class ClaimClient {

  private static final String nsString =
      ""http://www.myOrg.com/ClaimService"";
  public static void main(String[] args)
      throws Exception {
      try {
          Options opts = new Options(args);

          Service service = new Service();
          Call call = (Call)service.createCall();

          // set port to 8081 so that can observe
          // the messages with tcpmon
          // Java org.apache.axis.utils.tcpmon
          opts.setDefaultURL(""http://localhost:""
            + ""8081/axis/servlet/AxisServlet"");

          call.setTargetEndpointAddress
             (new Java.net.URL(opts.getURL()));

          Document doc = XMLUtils.newDocument();

          // set up the soap envelope
          SOAPEnvelope env = new SOAPEnvelope();
          // add an element for the method we'll
          // invoke needs to have ns = to the
          // path of the service
          Element anEl = doc.createElementNS(
              ""http://localhost:8080/"" +
              ""LogTestService"", ""testMethod"");
          SOAPBodyElement sbe =
              new SOAPBodyElement(anEl);
           env.addBodyElement(sbe);
          // and add an element that holds
          // the insurance Claim we're
          // submitting.  It isn't signed yet.
          Claim aClaim = Claim.populate();
          Element claimEl = aClaim.asDOM(doc);
          SOAPBodyElement sbe2 =
              new SOAPBodyElement(claimEl);
          env.addBodyElement(sbe2);

          // now change the envelope into a
          // signed envelope, byadding a
          // signature (in the SOAPHeader)
          // that refers to the AccidentReport
          // element added by aClaim.asDOM()
          env = new SignedSOAPEnvelope(env,
            ""http://xml-security"");

          // and invoke the web service method
          call.invoke(env);

      }
      catch (Exception e) {
      }
  }
}

Listing 4

package com.myOrg.ClaimService.Client;

...

public class SignedSOAPEnvelope
    extends SOAPEnvelope {

    static String SOAPSECNS =
        ""http://schemas.xmlsoap.org/""
        + ""soap/security/2000-12"";

    static String SOAPSECprefix = ""SOAP-SEC"";

    static String keystoreType = ""JKS"";
    static String keystoreFile = "".keystore"";
    static String keystorePass = ""security"";
    static String privateKeyAlias = ""georgia"";
    static String privateKeyPass = ""secret"";
    static String certificateAlias = ""georgia"";

    static {
        org.apache.xml.security.Init.init();
    }

    public SignedSOAPEnvelope(MessageContext
      msgContext, SOAPEnvelope env, String
      baseURI, String keystoreFile) {
        this.msgContext = msgContext;
        init(env, baseURI, keystoreFile);
    }

    public SignedSOAPEnvelope(SOAPEnvelope env,
                              String baseURI) {
        init(env, baseURI, keystoreFile);
    }
    // fill this object by adding a signature to
    // the passed envelope env, and serializing/
    // deserializing the result back into this
    // object
    private void init(SOAPEnvelope env, String
                      baseURI, String
                      keystoreFile) {
        try {
		…
            // create new header element to hold
            // the signature per WS-Security
		…
            // grab the contents of env as a
            // Document (which we'll later
            // parse back into this object) so
            // that we can operate at the
            // DOM level
...

            // get the keystore
            KeyStore ks =
              KeyStore.getInstance(keystoreType);
            FileInputStream fis = new
              FileInputStream(keystoreFile);
            ks.load(fis,
              keystorePass.toCharArray());
            // get private key from keystore
            PrivateKey privateKey =
              (PrivateKey) ks.
              getKey(privateKeyAlias,
              privateKeyPass.toCharArray());

            // find the new header element
            // (in doc) we added above
            Element soapHeaderElement =
              (Element) ((Element) doc.
              getFirstChild()).
              getElementsByTagNameNS(""*"",
              ""Header"").item(0);
            Element soapSignatureElement =
              (Element) soapHeaderElement.
              getElementsByTagNameNS(""*"",
              ""Signature"").item(0);

            // create a signature object
            XMLSignature sig =
             new XMLSignature(doc, baseURI,
             XMLSignature.ALGO_ID_SIGNATURE_DSA);
            // have it create a signature element
            // and append that as a child of the
            // header element
            soapSignatureElement.
                appendChild(sig.getElement());

            // set up the reference in the
            // signature element --
            // refers to the AccidentReport
            // element created elsewhere
            Transforms transforms =
              new Transforms(doc);

            transforms.addTransform(Transforms.
              TRANSFORM_ENVELOPED_SIGNATURE);
            transforms.addTransform(Transforms.
              TRANSFORM_C14N_WITH_COMMENTS);
            sig.addDocument(""#Ar1"", transforms,
              org.apache.xml.security.utils.
              Constants.ALGO_ID_DIGEST_SHA1);


            // add x509 certificate information
            // taken from the keystore
            X509Certificate cert =
              (X509Certificate) ks.
              getCertificate(certificateAlias);

            sig.addKeyInfo(cert);
            // including the public key
            sig.addKeyInfo(cert.getPublicKey());
            // and finally, sign the message
            sig.sign(privateKey);

            // now canonicalize the entire document
            // (envelope) and parse it back into
            // this object (an extension of
            // SOAPEnvelope)
            …
        } catch (Exception e) {
        }
    }

    ...
}"

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.

  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.