Implementing an XKMS client isn't difficult if you have all the required technologies and time to play with and learn them. For PKI veterans, working with XML encoding is much simpler than working with ASN.1.

There are two major challenges in developing an XKMS client.

  1. Support for all required technologies:
  2. Many languages include good support for XML parsers. XMKS does not present any additional requirements, so most parsers should suffice. SOAP support is also strong and rapidly increasing.

    By contrast, there are not many XML Signature [XMLDSIG] implementations available (, except maybe for Java. Before starting your own client, remember that for any missing XML Signature requirement, you will have to write your own code!

  3. Interoperability with the Public XKMS services
  4. Web Services are by definition distributed. Getting your client to work with more than one XKMS service may be your biggest challenge, unless you also develop an XKMS service simultaneously.

    Be prepared--debugging a Web service over the Internet requires patience, communication, patience (again), and humility (as most of the bugs are going to end up being on your side!)

Why C# ?

I chose to implement my XKMS client in the C# language. Why C# ?

Client Architecture

Web Service Description Language (WSDL)

One of the initial factors that made the .NET framework attracting is that it included a utility (wsdl.exe) that can create both client and server skeleton code from a WSDL file. So the first thing I tried was generating a client skeleton using the XKMS WSDL file. After some work trying to fix the generated code I decided to start completely from scratch for my client (side note: the code generated for the service was easier to fix and is still being used in my service although I'm not sure it worth my time). Well for my first contact with both web services and the .NET framework it is was kind of deceiving.

Don't get me wrong. WSDL are critical for Web services, and code generator tools are a great idea. But the current implementations (both the .NET and the one included with Delphi 6) aren't up to the job, at least not for the complexity of XKMS. The only tools I need are those that save me time.

My advice: Try the tools, but don't overestimate their importance, as even when they work, they don't do any magic--they just generate code.

XKMS methods

Back to the basics. There are five methods are defined in XKMS, the first two in X-KISS and the last one in X-KRSS.

Hey that makes only three!!! Well the X-KRSS Register method is overloaded to be used to Revoke and Recover key pairs (as well as registering them).

But as they are functionally differen,t I will treat them as five separate and equals methods.

Each XKMS method consists of a request (created by the client and parsed by the service) and a response (created by the service and parsed by the client). Each matches an XML document. Each of these XML documents is represented by an object in the client library. This model makes the source code very similar to the one previously generated by WSDL.EXE.

Method Request object Response object
Locate XKMSLocate XKMSLocateResult
Validate XKMSValidate XKMSValidateResult
Register XKMSRegister XKMSRegisterResult
Revoke XKMSRevoke XKMSRevokeResult
Recover XKMSRecover XKMSRecoverResult

As all requests and all response objects share many properties a common ancestor was created for both of them.

XKMSClient XKMSResult
XKMSLocate XKMSLocateResult
XKMSValidate XKMSValidateResult
XKMSRegister XKMSRegisterResult
XKMSRevoke XKMSRevokeResult
XKMSRecover XKMSRecoverResult

In XKMS, as in most Web services, the client has the responsibility of calling the service. Sending the SOAP request and receiving the SOAP response falls under the XKMSClient descendants. So another class is thus created as an ancestor of the XKMSClient, SOAPClient, that is responsible for all SOAP transactions.

Each of the XKMSClient descendants can call the service using one of its many querying methods. All those methods return a corresponding XKMSxxxResult object.

Example - Get the KeyValue for a specific KeyName

XKMSLocate xl = new XKMSLocate();
XKMSLocateResult xlr = xl.LocateByKeyName( "" );
Console.WriteLine( "KeyValue: {0}", xlr.KeyValue.ToXmlString( false ) );

Using querying methods, the XKMSClient objects can be used many times (like loops) to retrieve results without creating a new request object each time.

Integrity between requests and response is assured by using TransactionID. Each request generates a new GUID and checks the results for the same GUID. This easy mechanism can defeat replay attacks (as a 128bits GUID is hard to predict) and its use is totally hidden in the client library.

All this results into two assemblies. One for the SOAP stuff (hopefully reusable) and one for the XKMS library.

How to cope with the differences between current services

A deployed service won't change overnight just because you found a bug. They should be regularly updated with newer, more interoperable, code. But until then we must all live with those bugs. That may not be a big problem if you work with only one service but providing a client that interoperates with more than one service can be difficult.

My solution to this problem was to create a configuration file: xkms.cfg. This file contains the default XKMS service URL (as this is generally static and takes too much space on the command line). A second line contains a number. This number is a bit array for any parameters that differ between the services. Using a configuration file, a single client binary can actually be used for all three services: Verisign's, Entrust's and mine.

I sincerely hope that those numbers will disappear in the near future as the newer services become compatible with the specification. However I feel (from past experience) that I will need them at least until the XKMS specification attains RECOMMENDED status in the W3C.

Building an XKMS Client: Challenges and Solutions

There are many challenges when implementing a standard. Some are problems of interpretation while others concern the technologies you use during implementation.

Challenges with the XKMS Specification

Apart from minor inconsistency and typos in the current specification, I remember two issues that introduced complications.

What do I sign?

While this is not strictly an XKMS issue (as it involve SOAP, Canonicalization and XLM-DSig) I was, after an initial reading, sure that I could sign a part of the document (the KeyBindingAuth) in the complete XKMS SOAP request. The problem resides with the namespace and canonicalization. At least I'm not the only one who missed that one--the current Entrust XKMS service is suffering from the same problem. (This is when the XKMS.CFG "magic" number comes handy for the client).

As for the solution it is very simple: Sign your request then insert it into a SOAP envelope.

How do I transform the authentication code

I don't know if it's the spec or the fact that I'm French speaking and therefore not able to comprehend all the subtleties of the english language but it took me too many attempts to get this simple part right.

Just in case that I'm not alone in my situation here's the source code for the algorithm.

Source 1: Adjusting the shared secret

	StringBuilder sb = new StringBuilder();
	for ( int i = 0; i < asSharedSecret.Length; i++ )
		char current = asSharedSecret[ i ];
		if ( !Char.IsWhiteSpace( current ) && !Char.IsControl( current ) )
			sb.Append( Char.ToLower( current ) );
	return sb.ToString();

Source 2: Converting the shared secret into a key

protected byte[] HMAC_AUTHENTICATION = { 0x01 };
public byte[] HMACSharedSecret( string asSharedSecret )
	// we HMAC the authentication code so we don't store the real one on file
	byte[] code = System.Text.Encoding.UTF8.GetBytes( asSharedSecret );
	return hmac.ComputeHash( code, 0, code.Length );

Challenges with the .NET framework

This section describes the top 3 difficulties (not counting the previously discussed WSDL) I encountered using the .NET framework. Please keep it mind that the .NET framework is BETA software (and offer many stable functionalities not available in other languages). Much of the difficulties I encountered (and most probably all bugs as I reported them and had feedback for most) will be fixed before the final release of the .NET framework.

HMAC signing using .NET beta 2

This part of the .NET framework isn't supported, at least officially, by Microsoft due to lack of testing before the release of beta 2. However I hadthe chance to talk with some very supportive Microsoft technical people who helped me "uncover" this functionality.

By chance, the functions required to sign and validate an HMAC signature are present inside the framework. They just aren't visible, as they are declared private. You can see these functions (and more interesting stuff) by using the "IL DASM" (intermediate language disassembler) tool on the "System.Security.Dll".

In order to use these private functions you have to use reflection to bind to them at runtime. I won't go in details into reflection and its uses (as there are many good articles on the subject), but I will share the "how-to" with the community. (Much of the actual coding credits goes to Microsoft.)

Source 3: HMAC signing

XmlNodeList xnl = request.GetElementsByTagName("KeyBindingAuth");
if (xnl.Count > 0)
	// see previous sample code
	byte[] ss = System.Text.Encoding.Default.GetBytes( sAdjustedSharedSecret );
	byte[] key = hmac1.ComputeHash( ss );
	// code for computing HMACSHA1 XML signature
	HMACSHA1 hmac2 = new HMACSHA1( key );

	// the toolkit always sign the #proto element
	r = new Reference("#proto");

	sx = new SignedXml(request);
	// Beta 2 req'd code to invoke non-public Sig methods using HMACSHA1
	Type tt = sx.GetType();
	object[] arg = new object[1];
	arg[0] = (object)hmac2;
	// end beta 2 requirement

	xnl[0].AppendChild( sx.GetXml() );

Source 4: HMAC signature verification

// Create an HMAC object with the signing/verification key 
HMACSHA1 hmac = new HMACSHA1( keyRequestCode );  
// Create an SignedXml object refering to the XML document 
SignedXml sx = new SignedXml( xmlDocument ); 
// find the signature inside the XML document 
XmlNodeList xnl = xmlDocument.GetElementsByTagName( "Signature" );    
// Load the signed element into the SignedXml object 
sx.LoadXml( (XmlElement)xnl[0] ); 
// The next line should do the job when .NET hits RTM 
// bool bSign = sx.CheckSignature( hmac );
// Beta 2 req'd code to invoke non-public Signature method using HMACSHA1
Type tt = sx.GetType();
object[] arg = new object[1];
arg[0] = (object)hmac;
bool bSign = (bool)tt.InvokeMember( "CheckSignature", BindingFlags.InvokeMethod|BindingFlags.
NonPublic|BindingFlags.Instance,null,(object)sx, arg );
// end beta 2 requirement 
// bSign now contains the result of the signature verification 

Warning: This source code is unsupported by Microsoft (and myself).

The WebClient class requires a Content-Length header element

Remember when I said that I dropped the WSDL.EXE generated code? You thought that ended all my problems? Well, not exactly.

After some time I had my client working with the Entrust XKMS service (perhaps not so much "working" as just receiving a response). But I never received a response from the Verisign service. My client was waiting for a response until time-out.

The Verisign developers suggested that I use a network sniffer. I have known network sniffer to be really useful tool for any network related development and had used them several time. But I was so sure the problem was on the service side that I didn't think to use one (thanks Mark!).

As I hadn't used a network sniffer for some years (and remembered them to be high priced software or hardware) I was pleasantly surprised to find a freely available tool.

Side note: The software I used (and still use from time to time) is Analyser 2.1 [ANALYZER], which is based on WinPCap [WINPCAP], from Politecnico di Torino and has a good support for IP based protocols. Well I finally found out the hard way that the cute class I was using, WebClient, needed a Content-Length header in the HTTP response. Without one the UploadData method just wouldn't return.

While this may be a bad behavior from the service, returning a Content-Length is recommended it is not mandatory in HTTP protocol (for both 1.0 and 1.1), so I consider this as a .NET bug. Again I dropped the easy solution and wrote code, using lower-level classes, around this problem. This new code makes up a good part of the "poupou.soap.client" assembly). Since this writing, VeriSign's XKMS service has since been updated to rectify this problem.

Canonicalization Method

The .NET framework supports the latest XML-DSig specification (with the previous exception) but only the latest. While this normally isn't a problem, it can still cause some headaches when some benign things are changed, like URIs.

So the problem was that the Entrust service is using Java classes that use, exclusively, this older URI and .NET is using, exclusively, the newer one.

So what? Just change it? Wel,l the URI is part of the signature verified by the .NET class. If you change it, then forget about verifying the signature. If you don't change it, then the signature can't be verified.

Then add it to .NET: Good thinking! But it's not possible under beta 2. But wait! What did I do for HMAC signing... reflection, humm... Could reflection save me again? Yes! I just need to find where to add our new algorithm URI and match it with the existing canonicalization class... et voila, almost no new code and everything works fine.

Reflection's a lifesaver. So before drowning in generated code, try to reflect on your problem.

Testing the samples

Please note that this section presents the public XKMS services as of October 1, 2001.

Required tools

At a minimum you will require the .NET framework (beta 2), which is redistributable from Microsoft This will allow you to use the compiled samples on your own computer.

If you want to modify the samples or use the XKMS client library you'll need the full .NET framework beta 2.

This kit contains the C#, VB.NET and C++.NET compilers and a basic GUI debugger.

As a non-free alternative, but much more tempting, there is Visual Studio.NET with its full development environment, but if you only search for a C# editor with color syntax and basic project management take a close look at SharpDevelop [SHARP].

1. Creating a user key pair

The first sample doesn't use XKMS. Its sole function is to create a persistent key pair that can be used by the other samples.

To generate a new key pair enter the following command at the command prompt (change the email address for your own).

mxkeygen /store user /algo rsa /size 1024

This creates a CryptoAPI [CAPI] container named "" under the user's store and generates a 1024 bit RSA key pair inside this container. An XML file named "" is also created by the utility. Note that both the public and private keys are written into the file (none of the samples are designed to be secure).

Warning: Under .NET beta, 2 it's IMPOSSIBLE to import a key pair into a container using the framework, without using C/C++ or another language outside the framework. This is why using the CryptoAPI container is important for the persistence of the key between the sessions. This behavior should be fixed in the release version of the .NET framework.

2. Using our key pair with the Verisign service

2.1. Register
In order to use our new key pair the next step is to register it with a service. The actual keyname syntax to register is defined by the service. A URL is used by Verisign service. You can request Verisign keynames by emailing After receiving your response, replace my keyname in the following sample with one of your own keynames.

mxregister "
d7ea68c518b2602ca4bbca895826a7dd&" password revokekeypassphrase verisign.cer

If the registration is successful, a certificate file named "verisign.cer" is created.

2.2. Locate

One registered you should be able to locate your own key pair by using the returned keyname from the registration. XKMS gives you no warranty that the keyname you used to register will be the same as the keyname you must use afterward.

mxlocate /keyname "
VeriSign&department=XKMS Test&CN=
SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f" /c verisign.cer

C# Sample XKMS Locate - version 20010930
Copyright 2001 Sebastien Pouliot, All rights reserved.

Result: Success
Test&CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f

RetrievalMethod URI:
RetrievalMethod Type:
Certificate created in file: verisign.cer

The sample support locating a key pair using a keyname, a keyvalue (like the one present in your key file - see note) or a certificate (like the one returned in the registration process) or the URL returned in the RetrievalMethod of a previous response. However not all services can find the keyname using all those options.

Note: Your key file contains your private key. However the client is smart enough not to send your private key over the wire when it uses this file.

2.3. Validate

Validate is much like a high-end Locate. It does everything Locate does and more. (This is also true of the classes in the library). So we can use the same syntax from the previous Locate example with one exception: signing (/s keyfile). One thing special about the Verisign service is that it requires requests to be signed. Unsigned method are refused and an error returned.

mxvalidate /keyname " Test&CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f" /s /c verisign.cer

C# Sample XKMS Validate - version 20010930
Copyright 2001 Sebastien Pouliot, All rights reserved.

Result: Success
Status: Valid
NotBefore: 2001-09-23 00:00:00
NotAfter: 2002-09-23 23:59:59
KeyUsage: 7
Test&CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f
Test&CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f
KeyValue: AOcPtx2HrZzb4TefCaxfTX+RG908XX44+hHmsOL
RetrievalMethod URI:
RetrievalMethod Type:
Certificate created in file: verisign.cer

The sample support key pair validation using a keyid, a keyname, a keyvalue, a certificate or the URL of the retrievalmethod.

2.4. Revoke

There are normally three ways to revoke your key pair.

  1. Using the passphrase you choose at registration time.
  2. Signing the revocation request with your private key.
  3. Out-of-band. While this isn't possible using XKMS (or any other protocol) it could be the only way to revoke your key pair if you lost both your passphrase and your private key.

The Verisign service supports only the first option.

mxrevoke /keyname " Test&;CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f" /passphrase revokekeypassphrase

C# Sample XKMS Revoke - version 20011007
Copyright 2001 Sebastien Pouliot, All rights reserved.

Result: Success
Status: Invalid
KeyId: Test&am
p;CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f
KeyName: Test&
amp;CN=SEBASTIEN POULIOT2&issuer_serial=0e67920ff5c484353ceda5f40ea9f19f
KeyValue: AOcPtx2HrZzb4TefCaxfTX+RG908XX44+hHmsOLGSGc8ZgNj
2.5. Recover

The Recover method isn't supported in the Verisign service (neither in my client as it would be impossible to import the keypair back in the container, see previous warning). AFAIK no current service provides this ability.

3. Using our key pair with the Entrust service

The current Entrust XKMS service only supports the Register method so we will limit our tests to this method.

You can request your authentication code for your keyname with the Entrust service directly from their web site:

Your authentication code is static for your keyname (it won't change if you register yourself again using the same keyname). Mark it down on paper as this will save you time.

Refer to the previous section for further details with each command.

Note: As you are changing your web service provider, remember to copy the ENTRUST.CFG over the XKMS.CFG before trying these examples.

3.1. Register an RSA keypair

For our first example we'll reuse the same keypair generated for the Verisign service.

mxregister LH8F-J3IR revokekeypassphrase entrust.cer

C# Sample XKMS Register - version 20011007
Copyright 2001 Sebastien Pouliot, All rights reserved.

Result: Success
Status: Valid
NotBefore: 0001-01-01 00:00:00
NotAfter: 0001-01-01 00:00:00
KeyUsage: 0
KeyValue: 84OgMpezKh/xhZ9BVHgys32h9M8txBb7IXG/YX3MB+mI9PE6
RetrievalMethod URI:,o=Entrust%20XKMS%20Demo%20Service,c=US&serialnumber=985273039
RetrievalMethod Type:
Certificate created in file: entrust.cer


3.2. Register an server generated RSA keypair

As a last example, we're using the Entrust service with a server generated RSA kaypair.

mxregister /server LH8F-J3IR revokekeypassphrase entrust.cer

C# Sample XKMS Register - version 20011007
Copyright 2001 Sebastien Pouliot, All rights reserved.

Result: Success
Status: Valid
NotBefore: 0001-01-01 00:00:00
NotAfter: 0001-01-01 00:00:00
KeyUsage: 0
KeyValue: 65lEs2M4+S5+0qInjCZmsPmiPbWnxckT4oCS0qmKJzd1sS/V
RetrievalMethod URI:,o=Entrust%20XKMS%20Demo%20Service,c=US&serialnumber=985273045
RetrievalMethod Type:
Certificate created in file: entrust.cer

Note 1: This key pair won't be imported in your container (same old bug) but it will be available in the generated key file (keyname.pubkey - so it won't conflict with an existaing keypair).

Note 2: The notes on the previous example also apply to this example.

Client Availability

The client library and the samples are available from http://www24.brinkster/xkms/ under a BSD license.

Source code for all samples (C#), but not the library, is also included in the archive.


Building an XKMS client isn't very difficult if you have all the required base technologies and good support from people operating the services.

For me, having both the technologies and helpful people, the most challenging parts were learning C#, ASP.NET and SOAP and working around the .NET beta 2 bugs. All those challenges were expected and welcomed.

My biggest surprise was that I could write my entire client using the .NET framework beta 2. I wasn't, at least initially, expecting a client release this year. More than once I though I was stuck to wait for the RTM (release to manufacturing) version but I always found solution and/or help around any issues.

The .NET framework proved much more stable and usable that I could have imagined (at least for a beta release). Anyone working with Web services should start assessing the technology. Its biggest problem, for now, is poor documentation, particularly for all cryptographic functions, for which about no samples yet exist.

What PKI really needs is applications, and lots of them. Everything that eases the development of such applications is welcomed and profitable for many. The advent of XKMS shows much promise for the new generation of web services, including all the legacy applications now being rewritten to use XML (and probably looking for a underlying security architecture). The timing is great and the future looks bright even if only a small percentage of those applications make use, even indirect, of XKMS.


In the next installment I will introduce my own XKMS service written in C# and ASP.NET. Until then feel free to experiment my client with all public XKMS services.


I'd like to thank the many people from Microsoft, Verisign and Entrust who helped and encouraged me during my work. Without their help you wouldn't be reading this article. Another big thanks goes to Andrew Brown from Verisign as without him you'd be reading this article in a much poorer English.


Analyzer: a public domain protocol analyzer,
Microsoft CryptoAPI 2.0,
Microsoft System.Security.Cryptography namespace,
SharpDevelop : The Open Source Development Environment for .NET,
WinPcap: the Free Packet Capture Architecture for Windows,
XML Key Management Specification,
XML Signature Work Group,

About the author

Sébastien Pouliot is a Security Architect for Motus Technologies, Québec, Canada where he designs security products and consults in PKI and IT security with government agencies. His previous works includes implementation of standards regarding cryptography and smart cards, biometrics technologies, custom PKI designs for specific markets, and many security related projects as an employee of the Canadian Department of National Defense. He can be reached at