XKMS Messages in Detail
April 7, 2004
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:
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.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.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 |
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.