Archive for the ‘General’ Category

SAML, SSO and ColdFusion

Monday, May 12th, 2008

Overview

The following post is intended as an overview of how a clients IT department could integrate their intranet with our CMD system to provide a seamless single sign on. There are commercial systems that can do this and we have integrated with some of them. The following shows how to use the open source Apache XMLSecurity library to the same effect.

For an overview of SSO and SAML see my earlier posting. In this post I will illustrate an implementation using the SAML Post Profile, the Apache XML Security library and ColdFusion. (Since the Apache library is a java library, the JSP/java code is almost identical). There are two parts: the creation of the SAML on the intranet server (IdP) and the interpretation of this by an external service, or to use the correct terminology, service provider (SP).

Configuring the Idp

This article assumes that you have ColdFusion 8. Alternatively you could run Tomcat and use JSP although the syntax is different, the library calls are the same.

This implementation uses the Apache XML security library. Download and place in the WEB-INF/cfusion/lib directory for ColdFusion (for java into your applications lib directory), You probably already have many of the jar files and just need serializer.jar and xmlsec-nnn.jar, where nnn is the current version number.

Creating the SAML on the IdP

The starting point is a SAML assertion from the previous article. We need to add some real user data and time information. The users identity and other information to embed in the SAML would come from Active Directory, LDAP etc. For this example I’ve hard coded some values.

<cfscript>
    ssouser=StructNew();
    ssouser["email"]=”user@tagworldwide.com”;
    nowDateTime =
        #DateFormat(DateConvert(‘local2utc’,
          Now()),’YYYY-MM-DDT’)#
        & #TimeFormat(DateConvert(‘local2utc’,
          Now()),’HH:mm:SSZ’)#;
    nowDateTimePlus1 =
        #DateFormat(DateConvert(‘local2utc’,
          DateAdd(‘n’,1,Now())),’YYYY-MM-DDT’)#
        & #TimeFormat(DateConvert(‘local2utc’,
          DateAdd(‘n’,1,Now())),’HH:mm:SSZ’)#;
</cfscript>

The CFXML tag provides an easy way to populate the SAML

<cfoutput>
<cfxml variable=”samlAssertionXML”>
<samlp:Response
  xmlns:ds=http://www.w3.org/2000/09/xmldsig#chr(35)#
  xmlns:saml=”urn:oasis:names:tc:SAML:1.0:assertion” 
  xmlns:samlp=”urn:oasis:names:tc:SAML:1.0:protocol” 
  IssueInstant=”#nowDateTime#”
  MajorVersion=”1″ MinorVersion=”1″ 
  Recipient=”http://externalService/sso/post/
  ResponseID=”#CreateUUID()#”>
    <samlp:Status>
        <samlp:StatusCode Value=”samlp:Success”/>
    </samlp:Status>
    <saml:Assertion
        xmlns:saml=”urn:oasis:names:tc:SAML:1.0:assertion”
        AssertionID=”#AssertionID#”
        IssueInstant=”#nowDateTime#”
        Issuer=”#issuer#” MajorVersion=”1″
        MinorVersion=”1″>
        <saml:Conditions NotBefore=”#nowDateTime#”
            NotOnOrAfter=”#nowDateTimePlus1#”>
            <saml:AudienceRestrictionCondition>
                <saml:Audience>
                  http://intranet/IdP
                </saml:Audience>
            </saml:AudienceRestrictionCondition>
        </saml:Conditions>
        <saml:AuthenticationStatement 
           AuthenticationInstant=”2007-11-04T13:52:42Z”>
           <saml:Subject>
             <saml:NameIdentifier>#ssouser.id#
             </saml:NameIdentifier>
           </saml:Subject>
        </saml:AuthenticationStatement>
        <saml:AttributeStatement>
         <saml:Attribute AttributeName=”emailaddress”>
         <saml:AttributeValue>#ssouser.email#
         </saml:AttributeValue> 
         </saml:Attribute>
         <saml:Attribute AttributeName=”givenname”>
         <saml:AttributeValue>
             #ssouser.givenname#
           </saml:AttributeValue> 
         </saml:Attribute>
         <saml:Attribute AttributeName=”surname”>  
         <saml:AttributeValue>
         #ssouser.surname#
         </saml:AttributeValue> 
         </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>
</samlp:Response>
</cfxml>
</cfoutput>

The above uses an authentication statement for the users identity and attribute statements for additional attributes e.g. users full name, email address etc. The standard doesn’t define which attributes are used. It allows namespace attributes to be used, allowing complete flexibility.

To add a signature element, we use the XMLSecurity library. The ColdFusion variables need coaxing to extract the right java objects. The last line does the actual insertion of a signature element into the SAML.

<cfscript>
samlAssertionElement = 
  samlAssertionXML.getDocumentElement();
samlAssertionDocument =
  samlAssertionElement.GetOwnerDocument();
samlAssertion = samlAssertionDocument
  .getFirstChild();
SignatureSpecNS = CreateObject(“Java”,
  “org.apache.xml.security.utils.Constants”)
  .SigSpecNS;
Init = CreateObject(“Java”, 
   “org.apache.xml.security.Init”).Init().init();
XMLSignatureClass = CreateObject(“Java”, 
   “org.apache.xml.security.signature.XMLSignature”);
sigType =
   XMLSignatureClass.ALGO_ID_SIGNATURE_RSA_SHA1;
signature = XMLSignatureClass
   .init(samlAssertionDocument, 
    javacast(“string”,”"), sigType); 
samlAssertionElement
  .insertBefore(signature 
  .getElement(),samlAssertion.getFirstChild());

</cfscript>

Note that the signature is inside the the XML that we are signing; this is known as an enveloped signature. The following is needed by the XMLSecurity library to define that we will be using an enveloped signature.

TransformsClass = CreateObject(“Java”,
“org.apache.xml.security.transforms.Transforms”);
transformEnvStr =  
TransformsClass.TRANSFORM_ENVELOPED_SIGNATURE;
transformOmitCommentsStr =
TransformsClass.TRANSFORM_C14N_EXCL_OMIT_COMMENTS;
transforms = TransformsClass
.init(samlAssertionDocument
transforms.addTransform(transformOmitCommentsStr);
transforms.addTransform(transformEnvStr);

To sign the SAML, we need a certificate. A self self certificate can be used. Typically issuer signed certificates are used, and for this the cheapest certificate from Thawte etc is sufficient. Java typically stores these in a key store. You can follow one of the numerous guides to doing this via the command line or use Portecle which is very easy to use. The code to extract a certificate from a key store is as follows

ksfile = CreateObject(“Java”, “java.io.File”)
  .init(storepath);
inputStream = CreateObject(“Java”,
  “java.io.FileInputStream”)
  .init(ksfile);
KeyStoreClass = CreateObject(“Java”
  , “java.security.KeyStore”);        
ks = KeyStoreClass.getInstance(“JKS”); 
ks.load(inputStream,”sso”); 
key = ks.getKey(“sso”,keypw.toCharArray());
cert = ks.getCertificate(certAlias);
publickey = cert.getPublicKey();

We now need to add a digest, signature and key information.

signature.addDocument(“#chr(35)##AssertionID#”
  , transforms);
signature.addKeyInfo(cert);
signature.addKeyInfo(publickey);
signature.sign(key);

In this example we have signed the whole response. You will see examples on the web where first the saml assertion is signed, and then in addition the response is signed as well. This isn’t required when the assertion and response are created at the same time.

Finally, encode this into a HTML page that will do a post to the SP.

<html>
    <body onload=”document.forms[0].submit()”>
        <form action=
http://ServiceProvider/SSO/POST/” method=”post”>
        <input type=”hidden” name=”SAMLRequest”
        value=’#BinaryEncode(
           CharsetDecode(
           samlAssertionXML,”utf-8″)
           ,”Base64″)#’/>
        </form>
    </body>
</html>

Interpreting the XML on the External Service (SP)

On the Service Provider, we do everything in reverse.

First extract the SAML, parse the XML, and verify the signature. There are a few lines of code to set up the various java objects correctly; the last line does the work.

xmlResponse=CharsetEncode(
  BinaryDecode(form.SAMLRequest,”Base64″)
  ,”utf-8″);
docElement= XmlParse(variables.xmlResponse)
  .getDocumentElement();
SignatureConstants=CreateObject(
  “Java”, “org.apache.xml.security.utils.Constants”);
SignatureSpecNS=SignatureConstants.SignatureSpecNS; 
xmlSignatureClass = CreateObject(“Java”,
  “org.apache.xml.security.signature.XMLSignature”);
xmlSignature = xmlSignatureClass 
  .init(docElement.getElementsByTagNameNS
  (SignatureSpecNS,”Signature”)
  .item(0),”");
keyInfo=xmlSignature.getKeyInfo();
X509CertificateResolverCN = 
  “org.apache.xml.security.keys.keyresolver
  .implementations.X509CertificateResolverClass”).
keyResolver=CreateObject(“Java”,
   X509CertificateResolverCN)
  .init();
keyInfo.registerInternalKeyResolver(keyResolver);
x509cert = keyInfo.getX509Certificate();

isValid = xmlSignature.checkSignatureValue(x509cert);

Assuming the SAML has been correctly signed, we now need to establish that the request is from an approved source, is in the right time frame and isn’t a repeat. Finally extract the users identity.

ssoissuer = getSingleValue(XPathIssuer);
conditions = getSingleValue(XPathConditions);
beforeDate = DateConvertISO8601
  (conditions.XmlAttributes.NotBefore, 0);
afterDate = DateConvertISO8601
  (conditions.XmlAttributes.NotOnOrAfter, afterwindow);
certificate = getSingleValue
  (XPathCertificates).XmlText;
reference = getSingleValue(XPathUniqueReference);

// Verify Validity Info
// …

   // Extract User
   ssouser =  getSingleValue(XPathNameIdentifier).xmltext;

Much of the additional code for validity, verifying whether its a new or existing user etc will be very much application dependant.

The extras

The code above uses the function DateConvertISO8601, an implementation of this from David Satz can be found on cflib. The function getSingleValue is a wrapper around XMLSearch.

Finally, the class org.apache.xml.security.Init has an init initializer method that clashes with the ColdFusion init method; in CF 7 you need to call it via java introspection as in the following snippet (thanks to Jon for the correction on this):

InitClass = CreateObject(“Java”,
     “org.apache.xml.security.Init”);
emptyArray = ArrayNew(1);
initMethod=InitClass.GetClass()
  .GetMethod(“init”,emptyArray);
initMethod.invoke(Init,emptyArray);

<cffunction name=”getSingleValue” output=”false”
   access=”public” returntype=”ANY”>
   <!— Expects an array of values to be returned
   from XML data, but only returns the first one —>
   <cfargument name=”xpath” required=”true”>
   <cfargument name=”reqAttrib” required=”No”
     type=”boolean” default=”true”>
    <cfset values=
        XmlSearch(variables.xmlResponse,
        arguments.xpath)>
        <cfif reqAttrib >
         <cfif ArrayLen(values) EQ 0>
           <cfthrow type=”saml”
           message=”Error: No value for: #xpath#”>
        </cfif>
        </cfif>
        <cfif isArray(values)>
            <cfreturn values[1]>
        <cfelse>
            <cfreturn values >
        </cfif>
</cffunction>

More Reading

This PDF has a very clear java implementation. Phil Duba has a set of well written SAML articles on his blog. This uses a wrapper round a java implementation. You may also need to know about the differences between SAML1.1 and SAML 2.0: see saml.xml.org.

SSO with SAML

Wednesday, April 16th, 2008

Overview

We frequently get requests from clients to integrate our Tag:CMD system which a SAAS offering (Software As A Service) with their internal LDAP system. The purpose of the Blog is to describe how we approach SSO (Single Sign On)and to illustrate how it can be implemented.

The first part of the post is generic to SSO, after which, in a second post, I will illustrate an implementation using the SAML Post Profile, the Apache XML Security library and ColdFusion. (Since the Apache library is a java library, the java code is almost identical).

SSO has  a number of advantages:

  • Users want a single account and password to work on all their work systems
  • Reduces user maintenance in the application
  • Ensures user information is kept up to date
  • When users change employment their accounts are automatically deactivated

What is sometimes harder to appreciate is that its not as simple as calling their LDAP server. When evaluating a SSO solution we are looking for some key elements:

  • The clients server does the authentication to avoid
    • storing of users passwords in our database
    • forwarding of users passwords though our servers
  • The clients server sends us the following information
    • Users identity
    • Basic information about the user (e.g. Name, email etc)
    • Servers identity
    • Time stamp
  • Signatures, certificates and/or secret keys are used to protect against
    • Modification of the information
    • Replay of the information at a later point in time 
  • The system is decoupled so we don’t need a dedicated VPN or back channel to the users server

There are a number of standards for SSO including

  • OASIS: SAML
  • Liberty: ID-FF
  • OpenID
  • Etc

SAML

Many of these use SAML to exchange information. SAML looks complicated and verbose. Essentially its made up of a set of statements, called SAML Assetions. To this we add elements to provide identity and ensure the statements haven’t been modified.

The following illustrates the basic elements. Start with the user identity

<saml:AuthenticationStatement>
    <saml:Subject>
        <saml:NameIdentifier>DavidRutter</saml:NameIdentifier>
    </saml:Subject>
</saml:AuthenticationStatement>

This is incorporated into what is called a SAML Assertion; we can add time properties to this to define its validity period.

<saml:Assertion IssueInstant="2007-11-04T14:04:24Z">
    <saml:Conditions
        NotBefore="2007-11-04T13:59:24Z"
        NotOnOrAfter="2007-11-04T14:14:24Z"/>
    <saml:AuthenticationStatement>
      <saml:Subject>
        <saml:NameIdentifier>DavidRutter</saml:NameIdentifier>
      </saml:Subject>
    </saml:AuthenticationStatement>
</saml:Assertion>

The next steps involve signing the SAML assetions to verify who they came from and that they haven’t been tampered with. There are 3 parts to this:

  • The signed information
  • A signature of the signed information
  • A keyinfo to describe what was used for the signature

We create a digest of the SAML assetions using the secure SHA algorithm so ensure the SAML can’t be modified

<ds:SignedInfo>
    <ds:DigestValue>/UlguevI2sppqGHnuZQV</ds:DigestValue>
</ds:SignedInfo>

Sign the digest to prove the identity. The signing is done using the private key of a certificate.

<ds:SignatureValue>
ID0Pr3EMyqvLilnZ0YNzt3z8GEyz6029V11rq
</ds:SignatureValue>

We add a keyinfo section to identify what was used for the signature. This can be the actual certificate.

<ds:KeyInfo>
    <ds:X509Data>
        <ds:X509Certificate>MIIC6jCCAd</ds:X509Certificate>
   </ds:X509Data>
</ds:KeyInfo>

Putting the signature altogether looks something like this:

<ds:Signature>
  <ds:SignedInfo>
    <ds:DigestValue>/UlguevI2sppqGHnuZQV</ds:DigestValue>
  </ds:SignedInfo> 
  <ds:SignatureValue>
ID0Pr3EMyqvLilnZ0YNzt3z8GEyz6029V11rq
  </ds:SignatureValue>
  <ds:KeyInfo>
    <ds:X509Data>
        <ds:X509Certificate>MIIC6jCCAd…</ds:X509Certificate>
    </ds:X509Data> 
  </ds:KeyInfo>
</ds:Signature>

 

Profiles

Assuming we have a library capable of creating the SAML, the next part is to define how the user, the system creating the SAML (the issuer or IdP) and the system receiving the SAML (the service provider or SP)  interact.

There are a number of different profiles, the simplest architecturally is the Browser Post Profile.

  • User goes to an intranet site and is authenticated.
    (either automatically via, or using a username/password)
  • User clicks on a link to take them indirectly to the external service
    e.g. http://intranet/SamlRedirect?goto=ExternalServiceUrl
  • The link first goes to an intranet hosted page which generates the SAML, does a 64bit encoding, and generates a
    a HTML page with a javascript redirect to the external service
  • The users browser will automatically do a HTP POST of the SAML to the external service
  • External service decodes and verifies the SAML, and logs in the user automatically.

The html will look something like the following:

<body onload="saml.submit()" >
<form name="saml"
    method="post"action=http://serviceProvider/SSO/POST/ …>
    <input type="hidden" name="SAMLResponse" value="response" />
    …
    <input type="submit" value="Submit" />
  </form>
</body>

Implementation

This will be covered in a second post on how to do the encoding and decoding in Java and ColdFusion.

Links

See this wikipedia article for more information and links

Tag: invited to speak at ‘Scotch on the Rocks 2008′

Wednesday, December 12th, 2007

Tag: has been invited to speak at Scotch on the Rocks, a large developer conference for ColdFusion related technolgies. This will be a great opportunity for us to share what we have been doing in ColdFusion and Flex.

Both Simon Gladman and myself will be speaking. You can see us on the Scotch on the Rocks web site: Simon Gladman, David Rutter

One of my tasks as CTO is to help raise the profile of Tag: and its technical expertise.
We have been active in ColdFusion for many years, and have two very experienced ColdFusion development teams: one in London and one in Cape Town. This is the first time we have been invited to participate in a large developer event. Hope to see you there.

Welcome to the Tag: Blog

Monday, December 10th, 2007

Who we are

Tag: is an international design and production agency dedicated to helping clients and agencies create, manage and deliver their brand messages across the world. We bring consistency, efficiency and more than a little flair to complex global campaigns.

What we do

In a world where brand consistency, campaign synchronicity and time to market are key, Tag: enables you to produce and rapidly disseminate your creative ideas across disparate media and markets, ensuring you have the best press ad, web banner, TV commercial, poster, DM pack, leaflet or brochure in the right place at the right time.

Why We Blog

We do some great things at Tag: that we would like to share, including

  • Business Achievements
  • Product Announcements
  • Technical Advances