Whitepapers
Product Development

Calsoft Labs is a leading technology partner for companies, helping them develop new products and modernize existing ones using emerging technologies. more

Download PDF: XKMS Web Service Bookmark and Share

Securing Web Services using XKMS

Introduction

The XML Key Management Specification (XKMS) is a recent development in the cryptography domain, along the lines of the nearly ubiquitous XML-orientation of technologies. These days when everything is getting XML-enabled, why is this XML technology worth considering? Unlike many XML-enabled technologies, XKMS has the potential to revolutionize Public Key Infrastructure (PKI) by making PKI simpler to deploy, use and manage. In fact, XKMS just might make PKI a viable reality for enterprises, embedded devices and users.

If you have worked with PKI software from various vendors, then you'll be aware of the difficulties in setting up a solution and in getting different vendor software to interoperate properly. Today every vendor sells their own wares their own way, which are supposedly standard. You have the Java security package and APIs, Microsoft CAPI, RSA BSAFE, Xcert Xuda, and so on. And all these vendors provide their own certificate and key management solutions. Then there are smart card or cryptographic token extensions offered by various vendors. All these are PKCS compatible, and yet incompatible. An oxymoron surely? But how can one have an "infrastructure" if the pieces of the infrastructure from various vendors have a difficult time working together? Even within a vendor's solution, deployment is a big hassle. One needs to educate, and then explain away the "extra" burdensome steps required by users and administrators to configure, use and manage the solution.

XKMS brings in an important simplification by offloading complex key management tasks to web services and establishing access standards via XML. This offloading and XML usage also makes client code thinner and amenable to embedding into small devices.

However XKMS still doesn't fully address PKI adoption problems, though it goes a long way into making it simpler to use. Signing, verification and certificate management is outside the scope of XKMS by definition. The XKMS Java SDK provided freely by the consortium includes crypto functions in addition to key management. These are good for signing XML-based messages using the XML Signature Specification, and can therefore be used in conjunction with its primary APIs for Web Services. This, then, is the focus of this article.

In this article we describe the elements of XKMS, then focus on Microsoft .NET and how to apply the XKMS SDK to it. We show how one may use this SDK to secure SOAP calls on a Microsoft platform. Note that the SDK supports SOAP transport, which can be misleading in this context. It means that an XKMS web service can be reached through SOAP invocations. That is, the XML schemas used to access the XKMS web service may be SOAP compliant. It does not mean that SOAP invocations done by any client to any web service can be secured (signed/encrypted) by using XKMS SDK. That is a slightly more difficult problem and hence forms the main content of this article.

Now considering that this SDK is Java-based and that Microsoft has a well-known aversion to Java technologies, making this SDK work on Windows native technologies is a not-so-simple task. But we believe that Microsoft will soon include an XKMS compliant SDK into its offerings, considering that they are one of the co-authors of the specification.

Until then, Visual Basic and Visual C++ programmers may use the techniques presented in this article for their .Net services. Unix-oriented developers may also look at the samples to get familiar with the use of XKMS. For the brouhahas on XKMS, go to the mother lode at http://www.xkms.org maintained by VeriSign Inc.

Note that one really doesn't need the XKMS SDK to do all that is described here. Digital Signature related APIs abound, so why have we chosen to use XKMS? First of all, XKMS provides a simple and interoperable way, that we hope becomes a standard. Secondly, how can one resist a great demonstration of consuming one web service from another? And finally, even though SOAP can be carried over HTTPS for security purposes, SOAP message forwarding or proxying would not be possible without creating the risk of a man-in-the-middle attack vulnerability. With our solution, one can sign/encrypt any portion of a SOAP message and send it through an arbitrary number of channels. We hope that someone (maybe Microsoft) takes note and goes on to build a fully native solution bypassing some of the tricks that we have used to make our solution functional.

Click here to go to Top

XKMS Web Service

With XKMS, the complex PKI tasks are offloaded to a web service using XKMS APIs that uses XML. Verisign's implementation of XKMS web service is located at http://interop-xkms.verisign.com/xkms/Acceptor.nano.

The key functionalities of PKI, exposed by the XKMS web service includes:

  • Registering the signing key pair
  • Locating the public key
  • Validating the key
  • Key revocation
  • Key recovery

These authentication functions will reside in web servers that can be accessed from web-based programs that use the XML language. By allowing the XML application developer to delegate trust decisions to specialized processors, XKMS shields XML client implementation from the complexity of the underlying PKI. A simple configuration of the XKMS web service is illustrated in the figure ( figure 1) below.

Configuration of XKMS Web Service

As you can see in the above picture, XML is primarily used in communicating with XKMS web service. One can write one's own APIs as long as the XML interface to the XKMS web service is kept. For e.g. generating a key pair, securely storing it, creating a certificate, traversing and verifying certificate chains, checking whether a certificate has been revoked etc., is currently done with vendor-specific APIs. Microsoft CAPI, RSA BSAFE, Xcert Xuda, Java Security package etc., all have their own APIs and certificate and key stores and certificate servers. XKMS introduces an XML standard into their operation.

Click here to go to Top

Architecture of XKMS

There are two major subparts of the XML Key Management Specification:

XML Key Information Service Specification (X-KISS)

X-KISS defines protocols to support the processing of Key Information associated with a XML digital signature, XML encrypted data in an XML-aware application. Functions supported include locating required public keys given identifier information, and binding of such keys to identifier information.

XML Key Registration Service Specification (X-KRSS)

X-KRSS defines protocols to support the registration of a key pair by a key pair holder. Each of these protocols describes protocol exchanges that consist of a simple request and response exchange with a Trust Service.

Click here to go to Top

XKMS Digital Signature APIs

The XKMS Digital Signature APIs provide the set of classes and interfaces that you need to interact with the XKMS Web Service. The APIs, implemented in Java, enable client applications to access the cryptographic functions implemented by the web service. Let's have a look at some of the most useful classes in this API.

com.verisign.messaging.
XmlTransport

This interface provides Transport abstraction when invoking XML-based registration services

com.verisign.messaging.
XmlTransportSOAP

This class provides an XML-based transport to SOAP XML Gateways

com.verisign.xkms.
client.XKMSRegister

This class provides methods to client applications or applets, which provide a means of registering keys in an XML-based Public Key Infrastructure.

com.verisign.xkms.
client.XKMSLocate

This class can be used by an application to request (locate) and return a public key from a XKMS public key repository service

com.verisign.xkms.
client.XKMSValidater

This class provides methods to validate a public key in the XKMS public key repository service

com.verisign.xkms.
client.XKMSRevoke

This class provides methods to revoke a prior registered public key

com.verisign.xkms.
client.XKMSRecover

This class creates the request message to the XKMS registration server to recover a private key

com.verisign.xmlsig.
Signer

This class signs XML documents according to the W3C XML-Signature specification

com.verisign.
xmlsig.Verifier

This class verifies XML signatures according to the W3C XML-Signature specification

com.verisign.xmlsig.
tools.KeyConverter

This class converts from and to XML and Java formats. Use this class if you want to move a key to or from Java and W3C XML-Signature format

com.verisign.xpath.
Xpath

This class XPath encapsulates a W3C Xpath expression and namespaces that relate to the expression.

com.verisign.resource.
XMLResource

This class is responsible for abstracting the implementation of the underlying XMLDOM implementation from the user.

Click here to go to Top

Signing and Verifying XML Documents using XKMS

Any document that conforms to the standards of XML schema can be signed using XKMS. Before signing, you must register the signing key in the XKMS key repository. Once you have registered a key, you can perform all of the other XKMS functions (locate, validate, and revoke) against it.

Click here to go to Top

Registering the Keys in XKMS Key Repository

To register the keys in the repository, first generate a key pair (public and private key) and persist them into files. The code listing below (listing 1) shows how to generate RSA key pair in Java.

Listing 1: GenKeyPair.java


//Java application that generates RSAKeypair & persist 
them to files

/* Pls. Refer to Appendix-A to import the classes you
 need  */

public class GenKeyPair
{
 public static void main(String args[])
 {
 try
  {
   String pub_key_path=args[0];String pvt_key_path=args[1];
   KeyPairGenerator rsakeygen = 
   KeyPairGenerator.getInstance("RSA");
   rsakeygen.initialize(512);
   KeyPair rsakey =rsakeygen.generateKeyPair();
     
   Document pubdoc =
   KeyConverter.publicKeyToKeyInfo(rsakey.getPublic());
   Document pvtdoc =
   KeyConverter.privateKeyToKeyInfo(rsakey.getPrivate());

   FileOutputStream fos = new FileOutputStream(new  
   File(pub_key_path));
   XMLResource xmlres = ResourceFactory.getXMLResource();
   xmlres.publish(pubdoc, fos, XMLResource.FORMAT_PRETTY_XML);
   fos = new FileOutputStream(new File(pvt_key_path));
   xmlress.publish(pvtdoc, fos, XMLResource.FORMAT_PRETTY_XML);
  } 
  catch (Exception e) {e.printStackTrace();}
  }
}

use the following command to run the above code:

java GenKeyPair PublicKey.xml PrivateKey.xml

This command generates the key pair and persist them into the files, PublicKey.xml and PrivateKey.xml.

The persisted form of the public key and private key, which were generated using listing 1 are shown in the figures, figure 2 and figure 3 respectively.

Public key persisted to a XML file

Private key persisted to a XML file

The client can now register these keys in the XKMS repository.

Note: To register the keys in the repository, you require a key name, shared secret (also called as passcode), and pass phrase. Key name and shared secret can be obtained by contacting xkms-interop@verisign.com Pass phrase (also called, "challengephrase") can be any valid string (with no white space), chosen by the registrant and he must remember it, as it is used as an authorization phrase for revocation requests.

The source code that follows ( listing 2) shows how to register the client-generated keys in the repository.

Listing 2: RegisterKeys.java

//Java application that registers keys in XKMS 
Key Repository

/* Pls. Refer to Appendix-A to import the classes
 you need  */

public class RegisterKeys
{
  public static void main(String args[])
  {
    String service_url=args[0];
    String keyName = args[1];
    String sharedSecret = args[2];
    String passPhrase = args[3];
    String pub_key_path = args[4];
    String pvt_key_path=args[5];

    FileInputStream fis; Document doc; XPath xpath;
    try
    {
    //Retrieve the public key from persisted file
    fis = new FileInputStream(new File(pub_key_path));
    doc = ResourceFactory.getXMLResource().parseXML(fis);
    xpath= new XPath("/*");
    PublicKey pbkey = KeyConverter.keyInfoToPublicKey(doc,xpath);

    //Retrieve the private key from persisted file
    fis = new FileInputStream(new File(pvt_key_path));
    doc = ResourceFactory.getXMLResource().parseXML(fis);
    xpath= new XPath("/*");
    PrivateKey pvtkey = KeyConverter.keyInfoToPrivateKey(doc,xpath);

    KeyPair rsakey = new KeyPair(pbkey, pvtkey);
    XmlTransport transport = null;
    

    //Set the proxy setting if any
    System.getProperties().put("proxySet","true");
    System.getProperties().put("proxyHost","10.10.100.1");
	System.getProperties().put("proxyPort","3128");
	
	//Set the file where you want to place the trace results
   FileOutputStream debugos = new FileOutputStream(
          new File("/tmp/xkmsclient.out"));

    transport= new URLConnectionTransport(new URL(service_url),       
         debugos);
    XKMSKeyData data = new XKMSKeyData(rsakey, new
	XKMSKeyName(keyName));
    XKMSAuthInfo authInfo
     = new XKMSAuthInfo(passPhrase, sharedSecret);
    XKMSRegister register = new XKMSRegister(data, authInfo);
    XKMSRegisterResponse resp = null;
    
    // Register the keys in the XKMS repository
    resp = register.send(transport);
    
    if (resp.getStatus()) {
     System.out.println("The private key is "+resp.getPrivateKey());
      System.out.println("The public key is "+resp.getPublicKey());
    }
    }

Use the following command to run the above code:

java RegisterKeys http://interop-xkms.verisign.com/xkms/Acceptor.nano keyname sharedsecret challengephrase PublicKey.xml PrivateKey.xml

Where the first argument to this command, denote the URL of the Verisign XKMS web service to which the requests are routed.

Click here to go to Top

Signing a XML Document

Having registered the keys in the XKMS repository, the client can then use them to sign his documents. The class Signer in the API provides the essential methods needed to sign the Document.

Types of XML Signatures

Signed data can be located within the XML that includes the signature or elsewhere. Based on this location, there are three different types of signatures: Detached, Enveloping and Enveloped signatures.

  • Detached signature is a signature over a content external to the Signature element and is identified through an URI. This description applies also if the signature and the object reside in the same XML document as sibling elements.
  • An enveloping signature is a signature, which includes the object to be signed within it, and identifies it via an URI or a transform (typically XML Canonicalization or XPath).
  • ? An enveloped signature is enclosed inside the XML element it signs. The enveloped signature must take care not to include it's own value in the calculation of SignatureValue.

Let's get into the code. The piece of code that follows, digitally signs the element CreditCardNo in the DOM document 'signme_doc', with the signing key provided in the constructor.

Signer signer=new Signer(signme_doc, privatekey, publickey);
Xpath signloc=new Xpath("/Employee/CreditCardNo");
signer.addReference(signloc);
Xpath output = new Xpath("/Employee");                           
Document signed_doc = signer.sign(output);

In the above example, you can notice that XPath expressions are being used to select the node sets that are to be signed in the XML document. Also, we implicitly tell it to add the public verification key to its output. The signature will be applied over the node set pointed by the XPath expression.

You may also optionally supply a location in the resulting Document where the Signature will be placed. In the above example, the resulting signature will be placed in the location specified by the XPath expression output. In this case, the signature will be appended to the element Employee.

Since we supply an output location, an enveloped signature will be created. An enveloping signature will be created if you provide no location for the signature.

The code that follows ( listing 3),shows how a Java application digitally signs the XML document using XKMS API. First, a RSA key pair is generated on the client side. Then, the generated key pair is registered in the XKMS repository by invoking the XKMS web service. The registered key pairs are then used for signing the document.

Listing 3: SignByXKMS.java

// Java application that digitally signs a XML Document using XKMS
/* Pls. Refer to Appendix-A
to import the classes you need  */

public class SignByXKMS
{
 static PublicKey pubkey=null,reg_pubkey=null;
 static PrivateKey pvtkey=null,reg_pvtkey=null;
 
 public static void main(String args[])
 {	
  try
  {					
   String  sign_doc_path,service_url,keyName;
   String  sharedSecret,passPhrase, signed_doc_path;
   
   sign_doc_path = args[0]; signed_doc_path = args[1];
   service_url=args[2]; keyName=args[3];
   sharedSecret=args[4];	passPhrase=args[5];
   
   FileInputStream fis; Document doc; XPath xpath;
   /*Generate Keypair*/
   genKeyPair();	//  defined below
   
   /*Register the generated keypair in the XKMS repository*/
   registerKeys(service_url,keyName,sharedSecret,passPhrase);
   //  defined below
     
   /*Sign the Document with the registered key*/
   fis = new FileInputStream(new File(sign_doc_path));
   
   Document sign_doc = 
   ResourceFactory.getXMLResource().parseXML(fis);
   Signer signer = new Signer(sign_doc, pvtkey,reg_pubkey);
   XPath signloc = new XPath("/Employee/cardno");
   signer.addReference(signloc);
   XPath output = new XPath("/Employee");
   Document signed_doc = signer.sign(output);
   
   System.out.println("The Document is successfully signed ");
   
   /*Serialize the signed document to a XML file*/
   
   FileOutputStream out_nanobiz = new FileOutputStream(new 
   File(signed_doc_path));
   XMLResource xRes_signed = ResourceFactory.getXMLResource();
   xRes_signed.publish(signed_doc,out_nanobiz);
   out_nanobiz.close();
   System.out.println("The Signed XML Document is written to "+
   signed_doc_path);
  }
  catch(Exception e){e.printStackTrace();}
}
//Method that generates Key Pair to sign the document
 public static void genKeyPair() throws NoSuchAlgorithmException
 {
  KeyPairGenerator rsakeygen =
   KeyPairGenerator.getInstance("RSA");
  rsakeygen.initialize(512);
  KeyPair rsakey = rsakeygen.generateKeyPair();
  
  pubkey=  rsakey.getPublic();
  pvtkey=  rsakey.getPrivate();
  
  //Method that registers the generated Key Pair in the 
  XKMS Repository
  public static void registerKeys(String service_url, String
  keyName,String sharedSecret,String passPhrase) throws Exception
  {
  KeyPair rsakey = new KeyPair(pubkey, pvtkey);
  XmlTransport transport = null;
  
  // Set the proxy setting if any
  System.getProperties().put("proxySet","true");
  System.getProperties().put("proxyHost","10.10.100.1");
  System.getProperties().put("proxyPort","3128");
  
  //Set the file where you want to place the trace results
  FileOutputStream debugos = new FileOutputStream(
   new File("/tmp/xkmsclient.out"));
  transport = new URLConnectionTransport(new URL(service_url),
   debugos);
  XKMSKeyData data = new XKMSKeyData(rsakey, new
   XKMSKeyName(keyName));
  XKMSAuthInfo authInfo
   = new XKMSAuthInfo(passPhrase, sharedSecret);
  XKMSRegister register = new XKMSRegister(data, authInfo);
   XKMSRegisterResponse resp = null;
  
  // Register the keys in the XKMS repository
  resp = register.send(transport);
  
  if (resp.getStatus()) {
    reg_pubkey = resp.getPublicKey();
    reg_pvtkey=resp.getPrivateKey();
    
   }
  }
 }
 

Compile SignByXKMS.java and run the application using the command,

java SignByXKMS signme.xml signeddoc.

xml http://interop-xkms.verisign.com/xkms/Acceptor.nano keyname sharedsecret passphrase

In the above example, the XML document, signme.xml is signed using the registered keys. The signed document is then serialized into a file, signeddoc.xml.

Look at the following figures, which show the XML documents before (Figure 4) and after signing (Figure 5).

XML documents before signing

XML documents after signing

In the above signed document (figure 5), you can notice the signature embedded within element. Since we provided the public verifying key while signing, you can see the public key embedded within element inside the signature. indicates the portion of the XML document that is being signed. In the above sample, the element cardno of the document is signed.

Click here to go to Top

Signing multiple parts of XML Document with same Signature

An XML document may have several associated signatures. For instance, the code below signs the elements OrderNo and OrderDetails in the DOM Document, 'signme_doc', with the same key.

Signer signer = new Signer(signme_doc, privatekey, publickey);
Xpath signloc1 = new Xpath("/Order/OrderNo");
signer.addReference(signloc1);                       		
Xpath signloc2 = new Xpath("/Order/OrderDetails");
signer.addReference(signloc2);   
Xpath output = new Xpath("/Order");      
Document signed_doc = signer.sign(output);		

Click here to go to Top

Signing multiple parts of XML Document with different Signatures

Multiple parts of the document can be signed with different signatures by repeatedly signing the document, with each of the signature.

Signer signer1 = new Signer(signme_doc,privatekey1,publickey1);
Xpath signloc1 = new Xpath("/Employee/cardno1");
signer1.addReference(signloc1);              
Xpath signloc2 = new Xpath("/Employee/cardno2");
signer1.addReference(signloc2);             
Xpath output = new Xpath("/Employee");
signme_doc = signer1.sign(output);

/*Sign the resulting document with another key*/

Signer signer2 = new Signer(signme_doc,privatekey2,publickey2);
Xpath signloc3 = new Xpath("/Employee/cardno3");
signer2.addReference(signloc3);     
output = new Xpath("/Employee");  
Document signed_doc = signer2.sign(output);

The above example signs the elements cardno1 and cardno2 of the DOM document, 'signme_doc' with one signature, i.e. with the key pair privatekey1 and publickey1 and the element cardno3 by a another signature, i.e. with the key pair privatekey2 and publickey2

You can see the XML Document resulting from the above code in the figure (figure 6) below. You can observe two signatures embedded within the document each of which denotes a signature.

XML Document containing multiple signatures

Click here to go to Top

Verifying Signatures in a Signed XML Document

The signatures contained in the XML document can be verified using the class Verifier in the XKMS API. This class can verify all signatures in a document, one at a time, with either the verification key supplied in the document, or with a user-supplied key.

String ns[]={"dsig","http://www.w3.org/2000/09/xmldsig#"};
XPath signaturelocation = new XPath("//dsig:Signature",ns);, 
Verifier verifier = new Verifier(signed_doc
  signaturelocation);	
boolean isverified = verifier.verify(publickey);

In the above example, we try to verify the signature specified at the signaturelocation in the DOM document 'signed_doc', using the key provided by the user.

The listing given below ( listing 4), verifies the signatures contained in the signed XML document 'signeddoc.xml' ( figure 5).

Listing 4: VerifyByXKMS.java

//Java application that verifies a signed XML 
Document using XKMS

/*Pls. Refer to Appendix-A to import the classes you need*/

public class VerifyByXKMS
{
 public static void main(String args[])
 {	
 try
 {	
  Document signed_doc;
  String signed_doc_path=args[0];
  String service_url=args[1];
  String keyName=args[2];
	 
  // Retrieve the public key
  PublicKey pubkey = getPublicKeyFromXKMS(service_url,keyName);
	 
   /* Take the signed document from the persisted file */
   FileInputStream fis = new FileInputStream(new
   File(signed_doc_path));
   signed_doc = ResourceFactory.getXMLResource().parseXML(fis);
	 
   /*Verify the signed document*/
   String ns[]={"dsig","http://www.w3.org/2000/09/xmldsig#"};
   XPath signaturelocation = new XPath("//dsig:Signature",ns);
   Verifier verifier = new Verifier(signed_doc,signaturelocation);
   boolean isverified = verifier.verify(pubkey);
	 
   if (isverified == false)
   {
	System.out.println("signature can not be verified");
	System.exit(1);
   }
   System.out.println("Success");
   }
   catch(Exception e) { e.printStackTrace();}
   }
   // Retrieve the verifying key from the XKMS repository
   
   public static java.security.PublicKey 
   getPublicKeyFromXKMS(String
   service_url,String keyName) throws Exception
   {
   XKMSKeyInfo keyinfo=null;
   String responses[] = {XKMSLocate.KeyName,
   XKMSLocate.KeyValue};
   XKMSLocate locate = null;
   KeyPair signingKey=null;
   locate = new XKMSLocate(keyName, responses,signingKey);
      
   //Set Proxy settings, if any
   System.getProperties().put("proxySet","true");
   System.getProperties().put("proxyHost","10.10.100.1");
   System.getProperties().put("proxyPort","3128");
      
   //Set the file where you want to trace the transactions
   FileOutputStream debugos=new FileOutputStream(new
   File("/tmp/xkmsclient.out"));
   XmlTransport transport = null;
   transport=new URLConnectionTransport(new
   URL(service_url),debugos);
   XKMSLocateResponse lr = locate.send(transport);
   if (lr.getStatus())
    {
     java.util.List keyinfos =  lr.getXKMSKeyInfos();
     keyinfo = (XKMSKeyInfo)keyinfos.get(0);
    }
  return (keyinfo.getPublicKey());
  }
 } // End of class VerifyByXKMS

Compile and then run the above code using the command,

java VerifyByXKMS signeddoc.xml

http://interop-xkms.verisign.com/xkms/Acceptor.nano keyname

The first argument specifies the signed XML document in which the signatures are to be verified. The above code first retrieves the public verification key from the XKMS repository using the user supplied keyname and then uses that key to verify the signatures contained in the document

Click here to go to Top

Securing SOAP transactions in .NET using XKMS

XKMS and .NET Web Services

NET is Microsoft's platform for Web services. Simple Object Access Protocol (SOAP) is the messaging protocol used for communication in .Net web services. With the raising adoption of these web services, securing the underlying SOAP transactions forms the major challenge. With a digital signature attached to a specific transaction, companies will know that the transaction came from the right person and that it's authorized. So, in order to ensure secure .Net transactions, we believe that Microsoft will incorporate XKMS in its .Net architecture in the near future.

Click here to go to Top

Demo: Securing SOAP transaction between .NET web service and VB Client

Since SOAP message conforms to the standards of a XML Document, here we've attempted to secure the SOAP transaction involved in .Net web services using the same signature technique that we discussed in the previous sections.

Let's go through the sample that describes how the SOAP transactions between the .NET web service and service requestor; in our case, a VB client can be digitally signed using XKMS.

Abstract of this sample:

Before looking into the sample, let's give you a short brief of what we try to illustrate in this sample. We implemented a VB client that securely consumes the .Net web service.

The client digitally signs the SOAP request and invokes the web service. Since .Net web service currently provides no means to process the signatures, some sort of pre-processing needs to be performed on the SOAP request in order to verify the signatures. For this purpose, we implemented an ISAPI filter that will do the required pre-processing.

Document Conversion Web Services

In this sample, the client accesses the .Net web service namely, "Document Service" located at, http://203.129.222.82/ado.net/PdfService/Service1.asmx.

Let us briefly explain what the web service does. "Document Service" converts the documents from various formats like .doc, .html, .asp, .xml etc., to PDF format. It exposes only one method named "Convert Document", which accepts the URL of the document to be converted from the client. It then uses the Win32 API call, UrlDownloadToFile () to download that document. Then it converts the document to PDF format, hosts it and passes the URL of the converted PDF file back to the client. More details of this web service can be found at the following URL: http://www.calsoftlabs.com/whitepapers/uddi.html

Click here to go to Top

Developing the Service Requestor (VB Client) for Document Service

Step 1:

Accept the value of parameters to the method ConvertDocument of the web service, entered by the client. The parameters include,

  • JobId
  • Source_Url - Url of the document that is to be converted
  • Document Type - Format to which Source_Url to be converted
  • Preferred Name of the converted file (Optional)

Screen displaying the input parameters entered by client

Step 2:

Build the SOAP request from the input parameters. Here we used XMLDOM object to build the request.


Private Sub BuildSoapRequest(ByRef xmldom As MSXML2.DOMDocument)
' Build the SOAP Request Payload
Set pi = xmldom.createProcessingInstruction("xml", " 
version=""1.0""")
xmldom.insertBefore pi, xmldom.childNodes.Item(0)
Set objXMLroot = xmldom.createNode(1, "SOAP:Envelope", 
"http://schemas.xmlsoap.org/soap/envelope/")
xmldom.appendChild (objXMLroot)
objXMLroot.setAttribute "xmlns:soapspenc", 
         "http://schemas.xmlsoap.org/soap/encoding/"
    objXMLroot.setAttribute "xmlns:xsi", 
         "http://www.w3.org/1999/XMLSchema-instance"
    objXMLroot.setAttribute "xmlns:xsd", 
         "http://www.w3.org/1999/XMLSchema"
    Set objxmlbody = xmldom.createElement("SOAP:Body")
    xmldom.documentElement.appendChild (objxmlbody)
    Set objxmlmethod1 = xmldom.createNode(1, "ConvertDocument", 
        "http://tempuri.org/")        
    objxmlbody.appendChild (objxmlmethod1)
    Set objxmlparam1 = xmldom.createNode(1, "JobId", 
        "http://tempuri.org/")
    objxmlparam1.Text = Trim(tJobid.Text)
    objxmlmethod1.appendChild (objxmlparam1)
    Set objxmlparam2 = xmldom.createNode(1, "Source_Url", 
        "http://tempuri.org/")
    objxmlparam2.Text = Trim(tSource.Text)
    objxmlmethod1.appendChild (objxmlparam2)
    Set objxmlparam3 = xmldom.createNode(1, "Dest_Ip", 
        "http://tempuri.org/")
    objxmlparam3.Text = Trim(tDest_ip.Text)
    objxmlmethod1.appendChild (objxmlparam3)
    Set objxmlparam4 = xmldom.createNode(1, "Dest_Port", 
        "http://tempuri.org/")
    objxmlparam4.Text = Trim(tDest_port)
    objxmlmethod1.appendChild (objxmlparam4)
    Set objxmlparam5 = xmldom.createNode(1, "Pfd_Filename", 
        "http://tempuri.org/")
    objxmlparam5.Text = Trim(tPfd_Filename.Text)
    objxmlmethod1.appendChild (objxmlparam5)
    Set objxmlparam6 = xmldom.createNode(1, "DocType", 
        "http://tempuri.org/")
    objxmlparam6.Text = Trim(tDoctype.Text)
    objxmlmethod1.appendChild (objxmlparam6)
End Sub

Step 3:

Let's see how to digitally sign the SOAP request built in step 2.

Private Sub SignSoapMessage(ByRef xmldom As MSXML2.DOMDocument)
 Dim signresult As String
 'Locations to store signing and signed files
 clt_sign_file = "c:\xkms_work\Sign_ExtractSoap.xml"
 clt_signed_file = "c:\xkms_work\Signed_ExtractSoap.xml"
 ' Save the SOAP message
 xmldom.save (clt_sign_file)
 'Initialize the Java Bean
 Dim signer As New SoapSignerBean.SoapSignerBean
 'Call the method in the Bean that signs the SOAP message
 signresult = signer.SignSoap(clt_sign_file,
   clt_signed_file,service_url,keyname,sharedsecret,passphrase)
    
    If (signresult = "Success") Then
        ' If the SOAP request is successfully signed
        xmldom.Load (clt_signed_file)
    Else
        ' Display the error and exit
        MsgBox signresult
        cmdExit_Click
    End If
End Sub        

In the above procedure SignSoapMessage, we use a Java Bean named, "SoapSignerBean" defined below ( listing 5), to sign the SOAP request. We call this bean using Sun's Active-X Bean Bridge tool.

Listing 5: SoapSignerBean.java

// Java Bean that signs the SOAP message

/*Pls. Refer to Appendix-A to import the classes 
you need */

public class SoapSignerBean 
    implements java.io.Serializable
{
 public SoapSignerBean() {}
 public String SignSoap(String sign_soap_path, String 
 signed_soap_path, String service_url,
    String keyName,string
 sharedSecret,String passPhrase)
 {	
  try
 {	
  java.security.PrivateKey pvtkey;
  java.security.PublicKey pubkey;
	
  //Generate and Register the Key pair in XKMS			
  /*Please refer SignByXKMS.java (Listing 3) to see how to  
  generate, register and retrieve key pair from XKMS */
      
  /* Sign the Body of the SOAP message with the registered 
  key pair*/
  
  FileInputStream fis = new FileInputStream(new 
  File(sign_soap_path));
  Document signme_doc =
  ResourceFactory.getXMLResource().parseXML(fis);
  Signer signer = new Signer(signme_doc, pvtkey, pubkey);
  Xpath signloc = new Xpath("/SOAP:Envelope/SOAP:Body");
  signer.addReference(signloc);
  XPath output = new XPath("/SOAP:Envelope");
  Document signed_doc = signer.sign(output);
     
  // Serialize the signed SOAP message 
  /* Please refer to SignByXKMS.java (Listing 3) to see how 
  to serialize the signed SOAP message to "signed_soap_path" */   
  return "Success";
 }
 catch(Exception e){return (e.getMessage());}
}
} 
 

Step 4:

Post the signed SOAP request to the web service. The procedure below, cmdInvokeService_Click, which gets called on invoking the web service, illustrates how this is done.

Public sub cmdInvokeService_Click()	

Dim xmldom As New MSXML2.DOMDocument    
Dim XMLHTTP As New MSXML2.XMLHTTP

'Calling procedure BuildSoapRequest(defined above) to build  
'the SOAP Request
BuildSoapRequest xmldom

'Calling procedure SignSoapMessage(defined above) to digitally
'sign the SOAP request
SignSoapMessage xmldom        

'Sets the SOAP Request Payload
Soap_Request_Payload = xmldom.xml

'Build Service URL from specified Destination and Port
service_url = Trim(Combo1.Text) & "://" & Trim(tDest_ip) & ":" 
& Trim(tDest_port) & 
"/Ado.Net/PdfConversionService/Service1.asmx"
    
'Open connection to the .Net Service
XMLHTTP.Open "POST", service_url, False

'Set the SOAP-Specific HTTP Headers
XMLHTTP.setRequestHeader "SOAPAction", 
"http://tempuri.org/ConvertDocument"
XMLHTTP.setRequestHeader "Content-Type", "text/xml"
        
'Post the SOAP Request to the .Net Service
XMLHTTP.send xmldom

' Retrieves the SOAP response payload        
Soap_Response_Payload = XMLHTTP.responseText		
End Sub	

Step 5:

The signed SOAP request is pre-processed at the ISAPI filter located in the server, where the web service resides. We'll discuss later, in Section 6.2.2 on how this is being done. On successful verification of the signatures, the SOAP request is passed on to the web service.

The web service then processes the request. In this case, it converts the Source_Url to Pdf File. It responds with the Pickup_Url, where the client can pick up his converted pdf document. The screen below ( figure 8), displays the response returned by the web service.

displays the response returned by the web service

From the above screen (figure 8), you can observe that the document is successfully converted to PDF.

Below is the screen (figure 9), displaying the SOAP payloads that travel across the wire in this transaction. As you can see, the SOAP request payload is digitally signed. The highlighted portion shows the Signature that is embedded in the request.

Screen displaying SOAP payloads that travel across wire between VB Client and .NET web service

Click here to go to Top

Pre-processing the SOAP request

Since .Net web service, currently provides no means to process the digital signature, the 'signed' SOAP request needs to be pre-processed in order to verify the signatures contained in it. One way to do this is by using an ISAPI filter.

What is an ISAPI Filter?

An Internet Server Application Program Interface (ISAPI) filter is a replaceable DLL, which the server calls whenever there is an HTTP request. The filter application sits between the network connection to the client and the HTTP server, allowing us to control the data exchange between the IIS and the client. Please refer to section 9, for more information on ISAPI filters. Example applications of ISAPI filters include custom authentication schemes, compression, encryption, logging, traffic analysis or other request analyses.

Statutory Warning: Though we haven't considered security issues here, be warned that buffer overflows within ISAPI dlls are a sure-fire way of inviting worms into your web server. Make sure that all buffers you read into have their sizes checked and the amount of data read at any time limited to the buffer size. Certain MFC versions of ISAPI filter sample code are known to have this vulnerability.

Click here to go to Top

Writing ISAPI Filter to pre-process SOAP requests to .NET web service

The ISAPI filters are based on notifications that the IIS sends to it. Each notification handles a different type of data. The data is relevant to each stage of the request process.

When the ISAPI filter gets a notification from the IIS, we then can manipulate the notification's data. After we have manipulated the data, we can choose whether we want the IIS to continue to process the request. The notifications available from the IIS server are:

OnPreprocHeaders
XmlTransport

Notifies the filter that the server has preprocessed the client headers.

OnAuthentication

Authenticates the client.

OnUrlMap

Notifies a filter when a server is mapping a logical URL to a physical path.

OnSendRawData

Notifies the filter before raw data is sent from the server to the client.

OnReadRawData

Notifies the filter after raw data is sent from the client to the server, but before the server processes it.

OnLog

Logs information to a server file

OnEndOfNetSession

Notifies the filter that the session is ending.

OnEndOfRequest

This notification is called at the end of a single request within a session.

For our sample, we'll monitor the OnReadRawData and OnEndOfRequest notifications. Since we want to capture every SOAP request to the "Document Service", OnReadRawData is the most suitable notification.

The OnReadRawData notification will keep track of each SOAP call to the web service. It checks whether the SOAP request is signed and if so, attempts to verify each of the signatures in it, through calling a Java class. On successful verification of the signatures, it strip off the < Signature > tags contained in the signed SOAP request and pass it to the web service.

The OnEndOfRequest notification is used here to initialize the static members. The source codes for these two notifications are given below.

Source Code:

Any language that can create DLLs can be used to write ISAPI filter. We used VC++ in this sample.

Listing 6: ProcessSoap.dll

// ISAPI filter application that pre-process SOAP 
requests to the.NET Web Service
DWORD CProcessSoapFilter::
  OnReadRawData(CHttpFilterContext* pCtxt,
  PHTTP_FILTER_RAW_DATA pRawData)
{		
ptrIn = (char *) pRawData->pvInData;		
if(block == 1)	// Skip the HTTP Header
{}
else if(block == 2)  
{		
i=0;	
bodyindex = 0;
while(i < pRawData->cbInData)
{				
if(strnicmp(ptrIn + i, "< ?xml", 4) == 0)
soapflag=true;				
 if(soapflag)
 {
 if(bodyindex == 0)
 {
   body=(char*)malloc(strlen(ptrIn) + 50);
 }
 *(body + bodyindex) = *(ptrIn + i);
 bodyindex++;
 }
 i++;					
}		
if(soapflag)
{			
*(body + bodyindex) = '\0';		
//Save SOAP message into a xml file for 
  verification
				
hFile = CreateFile("c:\\myfile.xml", GENERIC_WRITE, 
 FILE_SHARE_WRITE,	NULL,CREATE_ALWAYS,             
 FILE_ATTRIBUTE_NORMAL, NULL);
bTest= WriteFile(hFile,body, sizeof(char)*strlen(body), 
 &dwNumWritten,NULL);	
bTest=CloseHandle(hFile);					

// Verify the signatures in the SOAP request

HRESULT hresult;	CLSID clsid;	
_bstr_t result;	IXMLDOMDocumentPtr xmldom;
IXMLDOMElementPtr m_pDocRoot;
IXMLDOMNodeListPtr signnodelist;
DWORD no_signature,index;
				
CoInitialize(NULL);
hresult=CLSIDFromProgID(OLESTR(
 "prNetVerify.clsNetVerify"),&clsid);
_clsNetVerifyII*t;			                   
 hresult=CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
__uuidof(_clsNetVerify),(LPVOID *) &t);			      
 if(hresult)
{
 result =t->VerifySoap("c:\\myfile.xml");		
 if(strstr(result,"Success")== 0)
   {
// If all the signatures are successfully verified
// Initialize COM	
::CoInitialize(NULL);
HRESULT hr = xmldom.CreateInstance(CLSID_DOMDocument);
   if (hr)
 {
// specify xml file name
CString strFileName ("c:\\myfile.xml");
// convert xml file name string to
//something COM can handle (BSTR)	
_bstr_t bstrFileName;
bstrFileName = strFileName.AllocSysString();

// call the IXMLDOMDocumentPtr's load 
//function to load the XML document
variant_t vResult;
vResult = xmldom->load(bstrFileName);
if (((bool)vResult) == TRUE) // success!
{
// Get the list of < Signature >  tags in the DOM              
signnodelist=xmldom -->
   getElementsByTagName("dsig:Signature");
// Get the no. of < dsig:Signature >

no_signature = signnodelist->length;	
 index = 0;
// Strip off all the < dsig:Signature > from the parent
IXMLDOMNodePtr signnode;                
DWORD node_count=0;

while(no_signature >= 1)
{
 signnode=signnodelist->Getitem(node_count);
 signnode->parentNode->removeChild(signnode);
 no_signature=no_signature - 1;
 node_count = node_count + 1;
}
m_message=xmldom->xml;					
char *temp,*post;
temp= (char*)m_message;
post = (char*) malloc(strlen(body)+50);
i=0;
while(i < strlen(m_message))
{
*(post + i) = *(temp + i);
i++;
}
while(i >  strlen(body))
{
  *(post + i) = *(temp + i);
  i++;
}
wwhile(i < strlen(body))
{
 *(post + i) = ' ';
 i++;
}
*(post + i) = '\0';
//Modify signed rawdata with Verified content
memcpy(pRawData->pvInData,(char*)post,strlen(post));
}
t->Release ();
CoUninitialize();
  }		
 }
}	
block++;	
// return the appropriate status code
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
DWORD CProcessSoapFilter::
  OnEndOfRequest(CHttpFilterContext* pCtxt)
{	
	block = 1;
	soapflag=false;
	return SF_STATUS_REQ_NEXT_NOTIFICATION;
}			
	

In the OnReadRawData notification of the above code (listing 6), we called a VB Active-X component, "prNetVerify.dll" (listing 7) to verify the signatures. For more information on how to call VB Active-X DLL from MFC, you can refer to http://codeproject.com/dll/vbactivexwithvc.asp.

Listing 7: prNetVerify.dll

Class Name:  clsNetVerify

Public Function VerifySoap
 (ByVal signed_file As String) As String
On Error GoTo Err_Handler
 'Initialize the COM wrapped C# component
  Dim obj As New SoapVerifier.cSoapVerifier
  'Call the method that verifies signed SOAP message
  VerifySoap = obj.VerifySoap(signed_file)
  Exit Function
Err_Handler:
  VerifySoap = Err.Description
End Function

The above Active-X DLL, calls a C# component "SoapVerifier.cs", (listing 8) to verify the signatures. The C# component executes a Java class named "VerifyByXKMS.java" (ref listing 4) that verifies the signatures in the SOAP request.

For more info on how to call a C# component from VB, refer to http://www.vcdj.com/upload/free/features/vcdj/2001/04apr01/cs0104/cs0104p.asp

Listing 8: SoapVerifier.cs

// C# application that calls a Java class 
to verify the signatures in 
// SOAP message

namespace SoapVerifier
{
	using System;
	using System.CodeDOM.Compiler;
	using System.IO;
	using System.Text;

  public class cSoapVerifier
  {
public cSoapVerifier() {}
public string VerifySoap(string svr_signed_file)
 {
 string outputfile, errorfile, strcmd, strLine;
 TempFiles tf = new TempFiles();
 int startindex;      StreamReader sr;        
  StringBuilder strBuilder = new StringBuilder();        
  	try
   {			            
 outputfile = "";
 errorfile = "";            
 strcmd = @"java -cp .;C:\Xerces-J-bin.1.2.3\xerces-
 1_2_3\xerces.jar;C:\XKMS\xmlpki\jars\xmlpki.jar;
 C:\jdk1.3\jre\lib\ext\jce1_2_1.jar VerifyByXKMS "
   + svr_signed_file;
 Executor.ExecWaitWithCapture(strcmd, tf, ref 
 outputfile, ref errorfile);            
 sr = File.OpenText(outputfile);
 while (sr.Peek() > -1)
  {
 	 strLine = sr.ReadLine();
     if (strLine != "")
      strBuilder.Append(strLine);		              
  }             
  sr.Close();
  File.Delete(outputfile);
  File.Delete(errorfile);
  return strBuilder.ToString();		
  }
  catch(Exception e)
  {
	return("ERROR:  "+ e.Message);
  }                   
 }
}	
}                  

In this sample, we have seen how the SOAP requests to a .NET web service can be digitally signed using XKMS. Also, we have shown how the ISAPI filter offloads the signature verification from the web service. This sample can be enhanced further to sign the SOAP response returned by the web service

Click here to go to Top

Benefits of XKMS

  • XKMS eliminates the need for programmers to purchase and integrate specialized tool kits from vendors, which only operate with those vendors' offerings.
  • By moving the key management to a trusted server, XKMS offloads the complexities of PKI implementation from the developer.
  • XKMS is compatible with standards for WSDL (Web Services Description Language) and SOAP (Simple Object Access Protocol).
  • XKMS service offloads CRL checking, ASN.1 parsing and certificate chain processing from the client.

Click here to go to Top

Conclusion

In this article, we discussed the features of XKMS and have shown with a working sample, on how this can be used to digitally sign the SOAP requests to a web service. Note that SOAP support of XKMS does not imply that one can use the XKMS SDK to sign and verify SOAP messages, but only that the interface to an XKMS web service is SOAP compliant.

We have also explained topics like, generating the keys, registering the keys in the XKMS repository, signing and verifying XML documents.

Click here to go to Top

References

Click here to go to Top

Appendix - A

//  Classes need to be imported

import com.verisign.resource.ResourceFactory;
import com.verisign.resource.XMLResource;

import com.verisign.messaging.XmlTransport;
import com.verisign.messaging.XmlTransportDoc;
import com.verisign.messaging.XmlTransportSOAP;

import com.verisign.xkms.client.XKMSAuthInfo;
import com.verisign.xkms.client.XKMSKeyData;
import com.verisign.xkms.client.XKMSKeyName;
import com.verisign.xkms.client.XKMSRegister;
import com.verisign.xkms.client.XKMSRegisterResponse;
import com.verisign.xkms.client.XKMSKeyInfo;
import com.verisign.xmlsig.tools.KeyConverter;
import com.verisign.xpath.XPath;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAKey;

import com.verisign.xmlsig.Signer;
import com.verisign.xmlsig.Verifier;
import java.util.Hashtable;
import org.w3c.dom.Document;
import org.xml.sax.InputSource; 

Click here to go to Top