Export (0) Print
Expand All

Implementing XML Key Management Services Using ASP.NET

 

Blair Dillaway
Microsoft Corporation

January 2002

Summary: This paper and its associated sample code cover creating a Microsoft ASP.NET Web service that conforms to a SOAP message-based interface specification. (12 printed pages)

Download Xkmscode.exe.

Contents

Introduction
Creating an XKMS Service
   Generating Service Prototype Code
   The XKMS Object Model
   The XKMS Service Implementation
XKMS SOAP Extension for XML Signature
   Incorporating Other Transforms
Building an XKMS Client
   Creating the XKMS Service Proxy
   The XKMS Client Implementation
References

Introduction

This paper and its associated sample code cover creating a Microsoft® ASP.NET Web service that conforms to a SOAP message-based interface specification. Specifically, the interface is defined by a Web Services Description Language (WSDL) document defining the SOAP messages and XML types used to communicate with the service. Increasing interest in XML-based Web services indicates that there will be many standard services defined using WSDL. This will provide the basis for building compatible service implementations that are accessible via a common client implementation.

In this paper, we examine one such Web service standard, the XML Key Management Specification (XKMS). XKMS defines interfaces supporting trust and discovery services for use with Public Key Cryptographic security solutions. These include support for public key registration/revocation, key roaming, key recovery, locating an entity's public key, and validating an entity's public key.

If you are familiar with ASP.NET Web services, you will recognize that most discussions start from the perspective of building a new Web service. It is assumed that you begin by writing the service application code and defining the publicly exposed methods. Using the service implementation, you then use the ASP.NET tools to automatically publish a WSDL description, create client proxy code, and so forth. The SOAP message formats used to communicate between the client and the service applications will conform to the WSDL document, but you are typically not concerned about exactly what these message formats are. It is sufficient they are consistent with the implemented code.

Having to build a Web service implementation conforming to predefined SOAP messages presents you, as the developer, with additional complications. It requires your careful development of the .NET object model and use of XML serialization attributes to insure conformant SOAP messages. To do this manually, starting from a WSDL document, a fairly deep understanding of WSDL, SOAP, XML Schema, and the ASP.NET SOAP serialization functionality is required. While many developers may understand these technologies, it still represents a significant burden that ASP.NET tools can significantly reduce.

Next we will walk through the creation of an XKMS-compatible service and client using the ASP.NET Web services tools showing you how to:

  • Create an object model starting from WSDL
  • Create prototype code exposing the public service interfaces
  • Create a client side service proxy from WSDL

Then we will show you how to create an XKMS service and client application, highlighting some interesting limitations in WSDL and the ASP.NET tools and discussing ways to working around these issues.

Creating an XKMS Service

Generating Service Prototype Code

The first step in generating an XKMS service is to obtain the XKMS WSDL document from the XKMS specification. For many services, the WSDL document will contain all the XML Schema information required. However, XKMS imports the XML schema defined by the W3C/IETF XML Signature specification. Because the current ASP.NET wsdl utility won't automatically retrieve imported schemas based on their URLs, you need to manually retrieve that document.

The wsdl utility can consume WSDL and XML Schema documents and produce conforming service prototype code. However, if you attempt this using the versions of the WSDL and XML Schema documents contained in the referenced specifications, wsdl will return an error due to out-of-date XML Schema usage. This simply reflects the rapidly evolving nature of XML standards, including the XML Schema standard, coupled with the recent development of the Microsoft .NET Framework.

Fortunately, this problem is trivial to fix. The solution is to bring both the XKMS WSDL schema and XML Signature schema documents into compliance with the latest XML Schema specification. This requires updating all instance of the XML Schema namespace in these documents to "http://www.w3.or/2001/XMLSchema" and updating the "type" definitions to match the latest schema specification. For our purposes, this amounts to changing:

  • integer to int
  • timeInstant to dateTime
  • uriReference to anyURI

(Note: Future versions of the XKMS WSDL and XML Signature schema are expected to be brought into conformance with the 2001 XML Schema specification.)

The updated XKMS WSDL document and XML Signature schema can be found in:

  • XSchema\xkms.wsdl
  • XSchema\w3c_xml_signature.xsd

To generate service prototype code, run the command (see XSchema\Autogen.bat):

wsdl /server /out:XKMSProto.cs xkms.wsdl w3c_xml_signature.xsd

The output of this command can be found in the sample code file XSchema\XKMSProto.cs. If you examine this file, you will find it contains an abstract class containing the service publicly exposed methods. It also contains C# type definitions providing an object model derived from the XML types used by XKMS. A quick scan of this file should be sufficient to convince you of the value of having the wsdl utility generate this code. Manually creating this, or equivalent code, from the WSDL and XML Schema documents would be both time consuming and complex.

The XKMS Object Model

Let us now examine the object model generated by the wsdl utility. In particular, we should explore some of the more interesting design patterns and places where WSDL limitations affect the implementation.

First, you will note the generation of objects for simple XML types (int, string, base64Binary, etc.) is very straightforward. These map directly to .NET Common Language Runtime (CLR) types (System.Int32, System.String, System.Byte[], etc.). The wsdl utility simply creates a field of the appropriate type with a name corresponding to the XML element or attribute name. Generation of enumerated types is similarly straightforward.

When faced with complex XML types, however, things are a bit more complicated. The issue here is how to represent these types when they may have a variable number and type of children for a given instance. The wsdl utility uses two basic design patterns for dealing with this:

  • For an XML complex type defined as a sequence of child elements, a C# class is generated with a field corresponding to each possible child. The type of each field is determined by its XML type. Attribute children are handled in a similar manner using fields. The KeyBindingType class shows an example of this. By setting a field value to null, there will be no corresponding XML element in the serialized form.
  • For an XML complex type defined as a choice of child elements, a somewhat different approach is required as shown by the KeyInfo class. In this case, an object[] type field (Items) is created to hold references to the children. If the .NET CLR types (i.e., String, Byte[]) for multiple children are the same, an associated enumeration array field (ItemsElementName) is also created. This field holds a description of the XML type in each array location and insures only legal XML types for this class are specified. If each possible child element is a distinct type, then the enumeration is not required as shown in the KeyValue class.

There is one potential problem with the wsdl generated objects for complex XML types defined by choice semantics. This is due to a limitation of the current wsdl utility. It only occurs if the complex type allows 'any' elements as valid children. This occurs for multiple XKMS types, for example KeyInfo. The wsdl utility generated C# classes place an XmlAnyElementAttribute on the object/object[] array field, to indicate 'any' is allowed. If there is an associated enumeration type, it will have a value of 'Item' as one of its members. This isn't the best construct for programmatically dealing with 'any' values and furthermore the generated code will throw a serialization exception when run.

Correcting this is not difficult, but requires manual edits. First, remove the XmlAnyElementAttribute from the associated object/object[] field and place it on a new field of type XmlElement. Using the KeyInfo class as an example, the autogenerated code:

public class KeyInfo {
    
    /// <remarks/>
    [System.Xml.Serialization.XmlAnyElementAttribute()]
    [System.Xml.Serialization.XmlElementAttribute("KeyName", typeof(string))]
    [System.Xml.Serialization.XmlElementAttribute("KeyValue", typeof(KeyValue))]
    [System.Xml.Serialization.XmlElementAttribute("RetrievalMethod", 
         typeof(RetrievalMethod))]
    [System.Xml.Serialization.XmlElementAttribute("X509Data", typeof(X509Data))]
    [System.Xml.Serialization.XmlElementAttribute("PGPData", typeof(PGPData))]
    [System.Xml.Serialization.XmlElementAttribute("SPKIData", typeof(string))]
    [System.Xml.Serialization.XmlElementAttribute("MgmtData", typeof(string))]
    [System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")]
    public object[] Items;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("ItemsElementName")]
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public ItemsChoiceType2[] ItemsElementName;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")]
    public string Id;
}

is changed to:

public class KeyInfo {
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("KeyName", typeof(string))]
    [System.Xml.Serialization.XmlElementAttribute("KeyValue", typeof(KeyValue))]
    [System.Xml.Serialization.XmlElementAttribute("RetrievalMethod", 
         typeof(RetrievalMethod))]
    [System.Xml.Serialization.XmlElementAttribute("X509Data", typeof(X509Data))]
    [System.Xml.Serialization.XmlElementAttribute("PGPData", typeof(PGPData))]
    [System.Xml.Serialization.XmlElementAttribute("SPKIData", typeof(string))]
    [System.Xml.Serialization.XmlElementAttribute("MgmtData", typeof(string))]
    [System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")]
    public object[] Items;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("ItemsElementName")]
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public ItemsChoiceType2[] ItemsElementName;
    
    [System.Xml.Serialization.XmlAnyElementAttribute()]
    public XmlElement[] Any;

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")]
    public string Id;
}

The corresponding enumeration type (ItemsChoiceType2) is then altered to remove the 'Item' value as follows:

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.w3.org/
       2000/09/xmldsig#", IncludeInSchema=false)]
public enum ItemsChoiceType2 {
    KeyName,
    KeyValue,
    RetrievalMethod,
    X509Data,
    PGPData,
    SPKIData,
    MgmtData,
}

A revised XKMS object model with the changes described above can be found in XService\bin\XKMSTypes.cs. After making these modifications there is one further simplification that can be made. In the Transform class, you will note that the types allowed in the object[] Items field are now all of type String. Hence, we can change the Items field to be of type String[], making it somewhat easier to use.

If you compare XKMSTypes.cs with XKMSProto.cs, you'll notice several other changes. Most important are the changes to the AuthUserInfoType and AuthServerInfoType classes and their Signature type fields (ProofOfPossession and KeyBindingAuth). These fields have been changed to String types and the Signature class, and supporting classes (SignedInfo, and Reference), removed.

These changes were not made due to limitations in the expressiveness of WSDL and XML Schema, not the wsdl utility. While these types of documents can accurately describe XML types and message interfaces, there is no way to express critical semantic relationships between types. In the case of XKMS, WSDL has no way of indicating that the ProofOfPossession and KeyBindingAuth XML Signatures (used in the Register request message) are computed over the message contents. Specifically, they are computed over the serialized XML representation of the Prototype parameter (of type KeyBindingType) and the Signature SignedInfo structure. They cannot be computed from data in the .NET Common Language Runtime objects.

One approach to computing/verifying the XML Signature values is having the application code handle the serialization of objects into XML, making sure the XML structures required to compute the XML Signatures are available. This approach was rejected as it fails to leverage the XML serialization and SOAP message generation functionality provided by ASP.NET, adds significant complexity to the application code, and makes application maintenance more difficult.

Rather, we recognize one can decouple the problem of XML Signature generation and verification from the rest of the XKMS application. Since the XML Signature operations require the SOAP message contents, they can be implemented as an ASP.NET SOAP Extension (see System.Web.Services.Protocols.SoapExtension). We also recognize that the signature operations are agnostic about the data being communicated in the Register request SOAP message. Hence, they can be handled in a way that is independent from any specific client or service implementation. An XKMS Signature SOAP Extension is provided in the sample code (see the XSigner directory) and described in the XKMS SOAP Extension for XML Signature section.

The XKMS Service Implementation

Returning to the wsdl utility-generated prototype code (XSchema\XKMSProto.cs), one finds an abstract implementation of the XKMS service publicly exposed methods. These are annotated with attributes to control the Web service name, XML namespaces, and parameter serialization.

Creating a service implementation from this is quite simple. We merely copy the KeyService class definition, and the associated attributes, into an ASMX file and add the required Web service declaration:

<%@ WebService Language="C#" class="XKMS.KeyService" %>

For the sample code, we've also placed the KeyService class in a namespace, 'XKMS'. This is not required, but helps make it clear what this 'KeyService' is for.

Next, we remove the 'abstract' keyword from the KeyService class and its methods. Finally, application code is added to each method. For the sample, a code-behind implementation is used. Hence, the methods in the ASMX file merely pass the input parameters to a related method in a code-behind XKMSService class. This approach provides a clean separation between the code describing the XKMS service's public interface and the application logic specific to the sample implementation.

In the implementation there are several XmlIncludeAttributes on the KeyService class. If you examine the 'types' included by these attributes, you will find they are types already used explicitly for parameter values of the public WebMethods. You may wonder why these are present. The answer is they're not required and could be deleted. These were generated by the wsdl utility due to a limitation in tracking the usage of array types when generating prototype code.

The service implementation discussed in this section will have one side-effect users should be aware of. If we allow ASP.NET to auto-generate a WSDL description from the service, it won't conform to the XKMS specification WSDL. Specifically, the ProofOfPossession and KeyBindingAuth type definitions will show up as string types, rather than complex types with Signature type children. This is the result of our decision to implement the XKMS Signature operations as a SOAP Extension. ASP.NET is unaware of the transform the XKMS Signature SOAP Extension implements and therefore can't reflect this in the WSDL it generates. This does not however, impact the conformance of the service implementation to the XKMS specification.

To deal with this, the service could suppress the generated WSDL and explicitly include the WSDL from the XKMS specification. ASP.NET-generated WSDL is controlled by the documentation 'protocol'. It can be blocked in the configuration file by including the following:

   <webServices>
         <protocols>
          <remove name="Documentation"/> 
      </protcols>
   </WebServices>.

However, one might wish to make the generated WSDL available as an assist to client application developers using the .NET Framework as discussed in the Building an XKMS Client section.

XKMS SOAP Extension for XML Signature

As discussed in the preceding section, the sample implementation relies on an ASP.NET SOAP Extension to handle XML Signature generation and verification. This is required to correctly handle the optional ProofOfPossession and KeyBindingAuth elements within a Register request message. By using a SOAP Extension for this functionality, one can build XKMS client and service implementations while leveraging a common implementation of these complex operations.

The XKMS Signature SOAP Extension is provided in the XSigner directory. This code is used to build the XKMSSigner Assembly that computes and verifies XML Signatures in accordance with the XKMS specification. XKMSSigner contains no application decision logic and is agnostic as to the data encoded in the SOAP message. It is designed to only process Register request messages and will not alter other SOAP messages that might be passed to it.

To understand the XKMSSigner implementation, the reader should be familiar with creating and using classes derived from System.Web.Services.Protocols.SOAPExtension and System.Web.Services.Protocols.SOAPExtensionAttribute. The processing done in these implementation classes is straightforward. They provide a way to indicate when the XKMSSigner should be invoked and provide support for calling the signing and verification routines as required.

The more interesting part of the XKMSSigner implementation is the signing and verification methods (see XSigner\XKMSSigner.cs). The majority of this code deals with checking the SOAP message structure and locating nodes required for the signature operation. These checks insure the input XML is valid SOAP and that the Body contains a Register request message. It also locates the Prototype element, the public key value inside the Prototype element when verifying, and the ProofOfPossession and/or KeyBindingAuth elements. It then creates the appropriate document context for the signing operations to insure interoperability. The actual signature computation/verification code is a relatively small part of the overall implementation.

When used with an XKMS client, the XKMSSigner needs several pieces of information to perform its job. These include:

  • The desired signatures. This is determined by the presence or absence of ProofOfPossession and KeyBindingAuth elements (as children of AuthUserInfo or AuthServerInfo). In the sample code, the XKMS client sets these fields to a value of "Create" to indicate the corresponding signatures are required, though any non-null string value would work. If these elements are present in the Register request SOAP message, then the appropriate Signature is generated which replaces any existing children of the element.
  • The client's private signing key. This is made available to the XKMSSigner by placing it in a named data slot on the Thread context. The data slot is named "POPKeyValue". This approach does make the private key value accessible to any code having access to the thread context and may represent an unacceptable security risk in some environments. There are other approaches to passing this information that are more secure. But, a discussion of these is beyond the scope of this paper.
  • The KeyBindingAuth 'secret' phrase prearranged between the client and service. The secret is used to compute the HMAC key value used in the HMAC-SHA1 XML Signature. The secret is made available by placing it in a Thread context data slot named "BindingSecret". Again, depending on the user environment, one may wish to use a more secure approach to passing this value.

For the service implementation, XKMSSigner needs only the 'secret' phrase used in generating the KeyBindingAuth HMAC key. The public key for verifying the ProofOfPossession signature is always contained in the Prototype element's KeyInfo child.

It is presumed that the KeyBindingAuth secret phrase may be different for every client, and possibly for each key registration attempt by a client. Hence, one can't determine the correct secret to use until the Register request message arrives. The XKMSSigner implements a method XKMSConfig.ResolveClientSecret (string keyname) to locate the correct secret. This is called from the signature verification method. The keyname parameter would presumably be either the KeyID or KeyName value, whichever the service has decided is most useful as an index into the list of issued secrets. The sample code for the ResolveClientSecret method would need to be modified to use whatever mechanisms the XKMS service creates for managing this list of secrets. This, of course, assumes the service supports the KeyBindingAuth mechanism.

Since the XKMSSigner code verifies signatures prior to the XKMS service code being run, we need a way to communicate the result of those verification operations to the service. The sample uses a straightforward approach. After verification of a signature, the corresponding Signature child element is removed from the ProofOfPossession or KeyBindingAuth element. A text value of "Success" or "Failure" is then inserted to signal the result of the verification operation.

To allow the user to view the XKMSSigner input and output SOAP messages, a simple logging facility is also built in. This is controlled by the presence of a value in the appSettings section of the configuration file. For example, the entry:

<add key="XKMSLog" value="c:\XKMS.Log"/>

tells the XKMSSigner to log information into the file C:\XKMS.Log.

Incorporating Other Transforms

You will note the XKMSSigner.cs sample code is largely concerned with validating and transforming the SOAP Register request message. Given that we're already transforming the message XML, one could easily incorporate other message transformation operations into this extension. A simple example is included in the sample code that alters the manner in which null values are encoded.

The SOAP specification allows one to indicate a null value in two basic ways. First, the corresponding XML element may be omitted. Second, the XML element may be included with an explicit 'nil' attribute with a value of "true" included. Consider the Prototype child of the Register request message. If the Prototype structure had a KeyID value of null, then one could either send (partial SOAP message shown):

<soap:Body>
   <Register xmlns="http://www.xkms.org/schema/xkms-2001-01-20">
      <Prototype Id="KB01">
         <Status>Valid</Status>
         ...
      </Prototype>
   </Register>
<soap:Body>

or

<soap:Body>
   <Register xmlns="http://www.xkms.org/schema/xkms-2001-01-20">
      <Prototype Id="KB01">
         <Status>Valid</Status>
         <KeyID xsl:nil='true'/>
         ...
      </Prototype>
   </Register>
<soap:Body>

Assume some XKMS Services have trouble deserializing messages using the nil attribute encoding (they shouldn't, but it's a simple example that allows us to demonstrate the technique). To adapt to this, XKMSSigner has the ability to remove elements with a nil attribute value of true. You can set a value in the appSettings section of the configuration file to control this (<add key="XKMSRemoveNils" value="true"/>). If requested, this operation is done prior to doing the signature computation, as any transform of the message after signature generation will most likely break the signature.

Building an XKMS Client

Creating the XKMS Service Proxy

There are two viable approaches to creating a XKMS service proxy for the client application. Either approach allows one to generate a client that is fully conformant with the XKMS specification.

First, one could run the wsdl utility against the XKMS specification WSDL similar to how the service prototype code was generated. The resulting proxy code would require manual edits, as described for the service, to make it usable.

Second, one could allow ASP.NET to generate a WSDL description for our sample service implementation and run the wsdl utility against this. Using this approach, no manual edits of the proxy code are required as the service WSDL already has the requisite modifications. This approach was used for the sample code.

The differences between the XKMS specification WSDL and the sample service implementation WSDL is discussed in the XKMS Service Implementation section. Obviously, as an XKMS client developer, you should only rely upon the XKMS specification WSDL being available. But use of the sample service WSDL can simplify your efforts.

The XKMS Client Implementation

Once we have the proxy for the XKMS service, building a client application is straightforward. The proxy contains the service exposed methods as well as all of the types required to use those methods.

The sample client code (see XClient/Client.cs) is a simple console application that can:

  • Register an RSA public key value with the service
  • Revoke an RSA public key value registered with the service
  • Locate an RSA public key(s) based on a KeyName value
  • Validate an RSA public key(s) based on either a KeyID or KeyName value

The user can perform a series of these operations with the application interactively collecting any required inputs.

Other XKMS-defined functionality is not implemented in the sample.

References

Microsoft .NET Framework SDK documentation

Simple Object Access Protocol (SOAP) 1.1, W3C Note 08 May 2000

W3C XML-Signature Syntax and Processing, W3C Candidate Recommendation 20 August 2001

Web Services Description Language (WSDL) 1.1, W3C Note 15 March 2001

XML Key Management Specification (XKMS) Version 1.1, W3C Note 30 March 2001

XML Schema Part 1: Structures, W3C Recommendation, 2 May 2001

XML Schema Part 2 : Datatypes, W3C Recommendation, 2 May 2001

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, AS TO THE INFORMATION IN THIS DOCUMENT.

Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

© 2001 Microsoft Corporation. All rights reserved.

Show:
© 2014 Microsoft