Zulfiqar's weblog

Architecture, security & random .Net

Archive for January, 2009

Part2: ACS Federation with Custom STS

Posted by zamd on January 23, 2009

In yesterday’s post I talked about ACS and how to interact with it as a standard web service, providing claims transformation functionality. Today I will show how we can federate ACS with our custom STS written using Geneva Framework. This architecture opens up many interesting scenarios. Here are the major steps involved in this architecture.

  • We need to establish a trust relationship between our local STS and Access Control Service (ACS). We can do this using ACS web portal. This is essentially just exchange of certificate(s)
  •  I have already created a simple STS, which runs on the same machine as my client, so it will issue tokens to any caller (anonymous or authenticated).
  • Once I got token from my local STS, I will pass this token to ACS, which will use the claims contained in this token to derive its transformation logic and will return a new token with different claims.
  • I can now present this new token to any web service or web site (which trust ACS) and get my job done.

Here is simple code, which first gets token from local STS and then uses it to get a new token from ACS.

private static void GetTokenFromACS Using SAMLToken GeneratedBy MyCustomSTS()

{

    // This is using WSFed binding – so WCF is doing it’s magic of intermediate token acquistion.

    var binding = new CustomBinding(“IssuedTokenForCertificate”);

   

    var acsCert = GetACSCertificate();

    var identity = new X509CertificateEndpointIdentity(acsCert);

 

    var epa = new EndpointAddress(new Uri(http://accesscontrol.windows.net/sts/eval01/saml for certificate), identity);

    var trustVersion = TrustVersion.WSTrust13;

    var clientCredentials = new ClientCredentials();

 

    WSTrustClient client = new WSTrustClient(binding, epa, trustVersion, clientCredentials);

    RequestSecurityToken rst = new RequestSecurityToken(RequestTypeConstants.Issue, KeyTypeConstants.Symmetric);

 

    try

    {

        RequestSecurityTokenResponse rstr;

        var token = client.Issue(rst, out rstr) as GenericXmlSecurityToken;

        var rpCert = new X509Certificate2(“zamdnetprivatekeycert.pfx”, “a”);

 

        var txtStat = ExtractSAMLAssertion(token, rpCert);

    }

    catch (Exception exp)

    {

        Console.WriteLine(exp.ToString());

    }

}

One important difference from yesterday’s post is: we are not using ACS solution credentials here at all (solution userId, password etc). Why? Because we are authenticating with our local STS, and ACS simply trust the token issued by our local STS. With the architecture, we can easily go and change our authentication mechansim with our local STS (go from Annonymous to kerberos) without effecting ACS.

This is how the binding configuration look like:

        <binding name=IssuedTokenForCertificate>

          <security authenticationMode=IssuedTokenForCertificate

            messageSecurityVersion=WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10

            requireSignatureConfirmation=false>

            <issuedTokenParameters keyType=SymmetricKey tokenType=http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1>

              <claimTypeRequirements>

                <add claimType=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name />

              </claimTypeRequirements>

              <issuer address=http://localhost:9000/STS binding=wsHttpBinding

                bindingConfiguration=AnnonyForCertificate>

                <identity>

                  <certificate encodedValue= +7y1Gj9Ta2w== />

                </identity>

              </issuer>

              <issuerMetadata address=http://localhost:9000/STS/mex />

            </issuedTokenParameters>

            <secureConversationBootstrap />

          </security>

          <httpTransport />

        </binding>

So you can see from the above that we are talking to our local STS using wsHttpBinding configured to allow anonymous access.

    <binding name=AnnonyForCertificate>

      <security mode=Message>

        <message clientCredentialType=None negotiateServiceCredential=false

          establishSecurityContext=false />

      </security>

    </binding>

By decrypting the token I can see following assertions returned by ACS.

<saml:AttributeStatement xmlns:saml=urn:oasis:names:tc:SAML:1.0:assertion>

  <saml:Subject>

    <saml:SubjectConfirmation>

      <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:holder-of-key</saml:ConfirmationMethod>

      <KeyInfo xmlns=http://www.w3.org/2000/09/xmldsig#>

        <e:EncryptedKey xmlns:e=http://www.w3.org/2001/04/xmlenc#>

          <e:EncryptionMethod Algorithm=http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p>

            <DigestMethod Algorithm=http://www.w3.org/2000/09/xmldsig#sha1 />

          </e:EncryptionMethod>

          <KeyInfo>

            <o:SecurityTokenReference xmlns:o=http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd>

              <X509Data>

                <X509IssuerSerial>

                  <X509IssuerName>CN=Root Agency</X509IssuerName>

                  <X509SerialNumber>53391460269745460375752793295988585963</X509SerialNumber>

                </X509IssuerSerial>

              </X509Data>

            </o:SecurityTokenReference>

          </KeyInfo>

          <e:CipherData>

            <e:CipherValue>mhZrKiIRoUyuwy8yXCMWq/xe3H8YyZOh4wMSMh3FknjIamC7QLBhiQAfV1DvQXNJhlY=</e:CipherValue>

          </e:CipherData>

        </e:EncryptedKey>

      </KeyInfo>

    </saml:SubjectConfirmation>

  </saml:Subject>

  <saml:Attribute AttributeName=action AttributeNamespace=http://docs.oasis-open.org/wsfed/authorization/200706/claims>

    <saml:AttributeValue>that’s done</saml:AttributeValue>

  </saml:Attribute>

</saml:AttributeStatement>

Now let me show you how I have established the trust relationship between my STS & ACS and how I configured ACS to return single claim in returned SAML token.

 

So the above rule says: If you get a token issued by zamd.net (which is my local STS) and it contains a Name claim with the value of Zulfiqar. Please send back an Action claim with the value of “that’s done”.  Because that’s the only rule dealing with my local STS that’s why the returned token has only one claim in it.

And finally this is how I established a trust relationship between my STS and ACS. I added a new issuer using the Portal, uploading my STSs public key certifcate to ACS and that’s it.

Now with this certificate, ACS can correctly validate that incomming token is indeed issued by my local STS(because STS signs the issued token), and then runs the transformation logic to choose correct set of claims to return back.

In this post, all the funcationality of going to the local STS, getting token and attaching it to the Issue call made to ACS is done by WSFederationHttpBinding. In the next post,I will show a more explicit way of achieving the same result, so that you can  have control over the token issued by your local STS, you can cache etc, and then forward it to ACS on your will.  Stay tuned…

Posted in .NET Services | Leave a Comment »

Access Control Service – Part1

Posted by zamd on January 22, 2009

.Net Services include a service known as “Access Control Service”, which is a cloud based Security Token Service (STS). 

ACS supports both active (WS-Trust) and passive (WS-Federation) clients.  In this post I will talk about WS-Trust support in ACS. 

Basic function of any STS is to accepts a token and return a different token essentially doing claims transformation. This claims transformation is also at the core of ACS. It use a very flexible rule based transformation engine to map input claims into output claims.  I have already signed up for .NET Services account and also downloaded & installed the SDK which includes some useful end-2-end samples. I will start with individual pieces and then try to connect them together in step by step manner. 

Ok let’s see how we can talk to ACS just like another web service? ACS token issuance functionality is available using many different bindings (you can easily get this list by using “Add Service Reference” command or svcutil.exe). 

Now thinking in terms of WCF: we already got a service, which is accessible over internet, so we need a WCF proxy to call it. 

Geneva Framework introduced a new class known as WSTrustClient – As the name suggest, this class is a WCF proxy for WS-Trust contract. If you can’t use Geneva Framework then this same functionality is also provided by WCF. Please see my other post on IssuedSecurityTokenProvider.  In this post – I’m going to use Geneva Framework because that’s the recommended approach for this scenario. Here is the code we can use to invoke ACS. 

private static void AcquireTokenFromACSUsingUserName() 

{ 

    var binding = new WS2007HttpBinding(“userNameForCert”); 

  

    //ACS(STS) signing certificate…        

    //Replace following string with actual ACS cert string 

    var certData = Convert.FromBase64String(“AwAAAAEOoik=”); 

    var acsCert = new X509Certificate2(certData); 

    var identity = new X509CertificateEndpointIdentity(acsCert); 

  

    var epa = new EndpointAddress(new Uri(http://accesscontrol.windows.net/sts/eval01/username for certificate”), identity); 

  

    var trustVersion = TrustVersion.WSTrust13; 

    var clientCredentials = new ClientCredentials(); 

  

    clientCredentials.UserName.UserName = “your solution userName”; 

    clientCredentials.UserName.Password = “your solution password”; 

  

    WSTrustClient client = new WSTrustClient(binding, epa, trustVersion, clientCredentials); 

    RequestSecurityToken rst = new RequestSecurityToken(RequestTypeConstants.Issue, KeyTypeConstants.Symmetric); 

    rst.AppliesTo = new EndpointAddress(http://zamd.net&#8221;); 

  

    RequestSecurityTokenResponse rstr; 

     

    // Send token issuance request to ACS. 

    var samltok = client.Issue(rst, out rstr); 

} 

By running above – I get a token back from ACS.  

  

  

Now Because I own the certificate for relying part (zamd.net) – I have written some code to decrypt the issued token to see the returned claims. Here is that code:  

    var rpCert = new X509Certificate2(“zamdnetprivatekeycert.pfx”, “a”); 

    var token = samltoken as GenericXmlSecurityToken; 

    var txtStat = ExtractSAMLAssertion(token, rpCert);  

Following SAML assertion is returned in the response token. 

<saml:AttributeStatement xmlns:saml=urn:oasis:names:tc:SAML:1.0:assertion> 

  <saml:Subject> 

    <saml:SubjectConfirmation> 

      <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:holder-of-key</saml:ConfirmationMethod> 

      <KeyInfo xmlns=http://www.w3.org/2000/09/xmldsig#> 

        <e:EncryptedKey xmlns:e=http://www.w3.org/2001/04/xmlenc#> 

          <e:EncryptionMethod Algorithm=http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p> 

            <DigestMethod Algorithm=http://www.w3.org/2000/09/xmldsig#sha1 /> 

          </e:EncryptionMethod> 

          <KeyInfo> 

            <o:SecurityTokenReference xmlns:o=http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd> 

              <X509Data> 

                <X509IssuerSerial> 

                  <X509IssuerName>CN=Root Agency</X509IssuerName> 

                  <X509SerialNumber>53391460269745460375752793295988585963</X509SerialNumber> 

                </X509IssuerSerial> 

              </X509Data> 

            </o:SecurityTokenReference> 

          </KeyInfo> 

          <e:CipherData> 

            <e:CipherValue>m7738wsrTe1GVNHl3wl1QEq4AJBERZY7GI7MMlWqRZBjM8T2UbrtK7M40+2JEm0JpgDMnVoq8Zhc8V5ui20lHKhAw2fOYSUBU529ZBzqKbYqDvIGApTXlp9ZVTvqS+Xo2kM315XtURkPHqmWvMA2eUUdZjA9tC+pvulWcuG2N4E=</e:CipherValue> 

          </e:CipherData> 

        </e:EncryptedKey> 

      </KeyInfo> 

    </saml:SubjectConfirmation> 

  </saml:Subject> 

  <saml:Attribute AttributeName=username AttributeNamespace=http://schemas.microsoft.com/strata/2008/09/accesscontrol/claims> 

    <saml:AttributeValue>Eval01</saml:AttributeValue> 

  </saml:Attribute> 

  <saml:Attribute AttributeName=action AttributeNamespace=http://docs.oasis-open.org/wsfed/authorization/200706/claims> 

    <saml:AttributeValue>Eval01Confirmed</saml:AttributeValue> 

  </saml:Attribute> 

  <saml:Attribute AttributeName=name AttributeNamespace=http://schemas.xmlsoap.org/ws/2005/05/identity/claims> 

    <saml:AttributeValue>Zulfiqar Ahmed (Orignal)</saml:AttributeValue> 

  </saml:Attribute> 

  <saml:Attribute AttributeName=upn AttributeNamespace=http://schemas.xmlsoap.org/ws/2005/05/identity/claims> 

    <saml:AttributeValue>Eval01</saml:AttributeValue> 

  </saml:Attribute> 

  <saml:Attribute AttributeName=name AttributeNamespace=http://schemas.xmlsoap.org/ws/2005/05/identity/claims> 

    <saml:AttributeValue>Eval01</saml:AttributeValue> 

  </saml:Attribute> 

</saml:AttributeStatement>  

  

So how does ACS decided to include these and only these claims in the returned token? Becase I have configured it using the portal- my portal configuration looks like following: 

  

  

Finally here is WCF binding used to talk to ACS.  

    <bindings> 

      <ws2007HttpBinding> 

        <binding name=userNameForCert> 

          <security mode=Message> 

            <message clientCredentialType=UserName negotiateServiceCredential=false 

              establishSecurityContext=false /> 

          </security> 

        </binding> 

      </ws2007HttpBinding> 

    </bindings> 

Posted in .NET Services | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.