Menu

XKMS Messages in Detail

April 7, 2004

Rich Salz

This month we begin to implement our XKMS server. We're going to focus on the Register operation. This is the cornerstone of the key registration service, X-KRSS; it implements the core CA function, which is issuing certificates.

The XKMS message formats are actually pretty forward-looking, when you consider that the project started a couple of years ago. XKMS messages are XML documents, as opposed to SOAP-RPC messages. They are independent of the transport or application protocol used to convey them. Further, they are defined in W3C XML Schema, and they all derive from a basic request or response abstract type. This isn't very unusual. SAML works the same way. But if you need to justify spending time on this, you can legitimately claim to be defining a service-oriented architecture (SOA) for your enterprise security infrastructure.

Depending on your job function, you might find that collection of buzzwords laughable, deplorable, or sexy. Speaking for myself, I can't even say "an SOA ITsec infrastructure" without giggling.

But if you're going to implement a request-response protocol that needs to be transport-neutral and might need to support "not done; try later" message exchange patterns, you should seriously consider re-using some of the XKMS types. Fortunately, the schema is set up to make this easy to do.

Request details

The type hierarchy for a registration request message looks like this:

  1. MessageAbstractType: This includes a URI for the service, which serves both to specify the endpoint and indicate versioning, and a Message-ID. Placeholders for a digital signature, opaque client data (encoded in base-64), and extension elements are also provided.
  2. RequestAbstractType: This extends the previous type to include information about how the server should respond, what protocol to use, and what information to return. Also included are elements and attributes to support asynchronous request-response, which is currently out of scope for this project.
  3. RegisterRequestType: The actual definition of a registration request, it extends the previous type to add the information needed to issue a certificate. We'll look at that in detail below.

The full schema definition can be found here. The Service attribute is important because it lets a single server instance support multiple security policies behind a generic URL. For example, certificates issued to a IPsec/VPN device would have a different lifetime and keyUsage extension than certificates issued to the accounting department for purchase orders. Using the Service attribute to identify different policies makes this simpler; it also means XKMS isn't dependent on HTTP. SMTP email doesn't have URLs.

In order for the XKMS service to issue a certificate, it needs to know some basic information. First, it has to know what the client wants the certificate to look like. This could include information like the validity period -- when the certificate is valid -- traditionally expressed as a pair of date/time values known as NotBefore and NotAfter. The dates are actually implemented as a half-open interval, so XKMS uses the more correct name of NotOnOrAfter. Other information here could include the user's name, information about a password to use in case the user believes her private key was compromised, and so on.

More from Rich Salz

SOA Made Real

SOA Made Simple

The xml:id Conundrum

Freeze the Core

WSDL 2: Just Say No

The next item is authentication: How does the server know that this request comes from a valid client? There are a number of ways this can be done. For example, a Registration Agent (RA) could sign the request. The RA could be a member of the Human Resources (HR) organization, in which getting a certificate is part of the initial employment orientation. In order for this type of mechanism to work, the signing key must already be known to the XKMS service.

Another type of authentication requires that the request use a plain text shared secret, that the client and XKMS server already know. For example, the client could enter their social security number and the XKMS server could verify that against the personnel database. In order for this to work, the shared data must be secret: the user should not share their SSN with anyone else.

Of course, this is only an example. SSNs are rarely kept very private, and certainly anyone in the HR department could find it out very easily. This means that an entire enterprise security infrastructure, its PKI, could be compromised by a secretary leaving for lunch without locking his filing cabinet. I used SSN as an example only to show the importance of not building a security tower on a foundation of sand.

The final piece of information is known as proof of possession or PoP. This requires the user to sign something with the private key, so that the server can verify it with the public key. In other words, the user is proving that they have the private key.

Method Dispatch

We'll implement the XKMS business logic in another module. Our first order of business is to add an import of that module to our Python-based server, adding import XKMS to the start of our file. We then modify the end of the do_POST method to do inline dispatch based on the XML element:

root = ps.body_root

n = root.localName

reply = StringIO.StringIO()

sw = SoapWriter(reply, nsdict=nsdict)

nsdict = { ...namespace-prefix/uri dictionary... }

if n == 'RegisterRequest':

    XKMS.Register(ps, root, sw)

else if n == 'RevokeRequest':

    XKMS.Revoke(ps, root, sw)

sw.close()

self.send_xml(reply.getvalue())

With this minor change, we're ready to implement the Register and Revoke operations. Each operation gets the general ZSI handle on the parsed SOAP object, in case it needs it. It also gets the root of the XML message and an output object to which to write its results.

The Slash-Dot Issue

In order to process requests, we'll need to look at a number of elements and their content from the request message. A partial list is included here, written in XPath syntax, where ds is a namespace prefix for the XML Digital Signature standard.

  • /RegisterRequest/PrototypeKeyBinding/ds:KeyValue/ds:RSAKeyValue : the RSA public key.
  • /RegisterRequest/PrototypeKeyBinding/UseKeyWith: this specifies how the client wants to use the key. In this case, the client will specify an X.509 certificate and the DN (name) they want:
    
    <UseKeyWith Application="urn:ietf:rfc:2459"
    
    Identifier="CN=Rich Salz"/>
  • /RegisterRequest/ProofOfPossession/ds:Signature: the digital signature on the request message that proves the client has the private key related to the public key in item one, above.

We could use the XPath notation directly and walk through the DOM:


from xml import xpath

from xml.xpath.Context import Context



# Set up namespace bindings and an XPath context

nsdict = {

    '': 'http://www.w3.org/2002/03/xkms#'

    'ds': 'http://www.w3.org/2000/09/xmldsig#'

}

RootContext = Context(ps.root, processorNss=nsdict)



# Search through the DOM tree, get the values we want.

UseKeyNode = xpath.Evaluate(

        '/RegisterRequest/PrototypeKeyBinding/UseKeyWith',

        context=RootContext)

appstring = UseKeyNode.getAttributeNS('', 'Application')

idstring = UseKeyNode.getAttributeNS('', 'Identifier')

This is amazingly painful. It would be much better if we could write code that looked like this:


req = parse(ps.root, 'file:///tmp/xkms/wsdl')

appstring = req.PrototypeKeyBinding.UseKeyWith.Application

idstring = req.PrototypeKeyBinding.UseKeyWith.Identifier

The difference is that XPath uses a slash to separate expression steps and the schema/WSDL parser generates objects that use the period to pull items out of a structure. I first saw the term -- the "slash-dot issue" -- on a weblog and would love to find a link to the original reference.

Fortunately for us, ZSI now has a tool that will create these mapped objects for us. Next time we'll use it to get a fully-parsed request message.