Thursday 21 February 2008

Token expiration dates

Here's another (brief) problem regarding the setting of validity dates within a token, or more specifically an RSTR (Issue), as implemented in WCF.

I was attempting to set validity date information within an RSTR. Firstly, I updated the dates within the SAML assertion itself, like so



DateTime validFrom = DateTime.UtcNow;

DateTime validTo = validFrom + TimeSpan.FromDays(1);
SamlAssertion assertion = new SamlAssertion();
assertion.Conditions = new SamlConditions(validFrom, validTo);

Avid readers (!) will note that the WS-Trust schema defines the following elements for specifying the valid date range of the accompanying token:

wst:RequestSecurityTokenResponse/wst:Lifetime


wst:RequestSecurityTokenResponse/wst:Lifetime/wsu:Created


wst:RequestSecurityTokenResponse/wst:Lifetime/wsu:Expires





where wst namespace is http://schemas.xmlsoap.org/ws/2005/02/trust

and wsu namespace is
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd


It is these dates that WCF will use to determine the lifetime of a token. The trick, when setting these dates in your RSTR, is to supply them in the correct format. If you don't you'll receive a very unhelpful error message within your client, the gist of which will be 'I don't understand your date formats, but I'm not going to tell you why'. After alot of messing, and finally resorting to code decomposition, I determined that the correct format is:

time.ToUniversalTime().ToString("o");
The RSTR is modified like so (within the OnWriteBodyContents method of the RequestSecurityTokenResponse class as per the WCF samples), for completeness:



// This part tells WCF what the lifetime of the token is, without having to parse the token itself
writer.WriteStartElement("Lifetime", TrustNS);
writer.WriteElementString("Created", UtilityNS, FormatDate(validFrom));
writer.WriteElementString("Expires", UtilityNS, FormatDate(validTo));
writer.WriteEndElement();



I guess that, with hindsight, it is reasonably obvious that the time should be UTC, but as they say, hindsight is a wonderful thing.


Hopefully, this will save somebody at least a small amount of time in future!


Fluffy

SAML token serialization

So, as promised, my first post is regarding simple serialization/de-serialization of a SAML token.





The driver for this exercise was to implement the 'Renew' verb of the WS-Trust specification, using the framework that WCF provides. To my endless frustration, the development team over at Microsoft stopped short of implementing the entire WS-Trust spec for anything but SCTs! Instead we're left with just 'Issue'.





My STS is signing tokens using self-issued X509 certificates (one for the STS and one for each client). Here's the (shortened) code for the serialization of the token:




///
/// Write the given token into an XmlWriter object
///

///
///
public virtual void WriteToken(SamlSecurityToken token, XmlWriter writer)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
serializer.WriteToken(writer, token);
}


Now, when I receive the token back from the client for renewal I need to de-serialize it in order to update its validity time span. Here's the code for this:



///
/// Read the token from an XmlDictionaryReader reader
///

///
///
public virtual SamlSecurityToken ReadToken(XmlDictionaryReader reader)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
System.Collections.Generic.List tokens = new System.Collections.Generic.List();
tokens.Add(SigningToken);
tokens.Add(UnencryptingToken);
System.IdentityModel.Selectors.SecurityTokenResolver resolver =
System.IdentityModel.Selectors.SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokens.AsReadOnly(), false);
SamlAssertion assertion = new SamlAssertion();
assertion.ReadXml(reader, new SamlSerializer(), serializer, resolver);
return new SamlSecurityToken(assertion);
}



Note that here the signing token is the certificate used by the STS to sign the assertion, and the unencrypting token is the private key equivalent of the certificate used to encrypt the proof key in the RSTR.

The point here is that in order to de-serialize the token, you're going to need the private key of the certificate used to encrypt the original. I guess this isn't a problem when you're in control of client certification, but I think you can see why this might cause issues.

Welcome!

Hi guys!

I've recently been doing a lot of work around Identity in the .NET framework, especially as it applies in 3.0 and 3.5. I've found that much of this stuff isn't covered in any great detail in the documentation. To get a problem solved I've simply had to 'suck it and see', if you'll pardon the expression. This has been frustrating, to say the least! I finally decided to blog about some of my exploits with SAML, WCF, WS-Trust, etc, in order that I might be able to help out a few people who come across the same issues!

I'll begin posting in the very near future - I'll start with a simple SAML serialization/de-serialization problem I encountered a few months ago. I also want to share my experiences of WS-Trust and how it applies (or doesn't!) to WCF, plus one or two salutory tales of developing an STS (Security Token Service) from scratch.

Cheers

Fluffy