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.
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.

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.
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.
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.
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.
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.


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.
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).


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.
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);
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.

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
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.
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
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)

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.

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.

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.
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
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.
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.
References
- Verisign's XKMS site
- Microsoft's .Net site
- W3C Specification on SOAP
- E-Mail ID to subscribe to, discussion forum for XKMS developers
- Creating ISAPI applications with Microsoft Visual C++
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;


