Implementing a WS-Federation scenario with WSE

Federation is key concept that allows to enable collaboration across multiple security or trust realms.
There are many resources on Internet about this topic so I won't enter in details during this post.
The solution that I will give is based on the second extension of this pattern:


http://msdn.microsoft.com/practices/default.aspx?pull=/library/en-us/dnpag2/html/wss_ch1_brokauthsts.asp


By the way, this is one of the security patterns published by the Pattern and Practices team in Microsoft.
This a good starting point if you want to be familiar with different and useful patterns for web services.


Sample scenario

Imagine the following scenario:


Fabrikam has exposed some services to make on line operations, such as purchase goods, make orders and others.
These services can be used only by customers so the access to them is restricted.
Contoso is one of Fabrikam's customers and it has developed an internal desktop application to consume some of these services.
As you can see, this is a typical scenario for Federation where we have both companies in different security domains or realms but they trust each other.


The services exposed by Fabrikam authenticate the users by means of X509 certificates, so one certificate is used for the client(Contoso) and another for the service(Fabrikam).
Contoso has deployed the desktop application in almost 100 machines and therefore installing both certificates in each one is not a practical solution in this case.




The Brokered Authentication pattern by means of security token services meets really well the requirements for this solution.


The arrows in this figure only show the work flow path and not the real path of the interchanged messages (Pair of request and response messages).


1. Contoso deploys a STS with both certificates and the desktop application can ask for a SAML token to this service using a UsernameToken, a Kerberos token, or other kind of client credential.
2. The STS in the Fabrikam's domain only accepts SAML tokens signed by a well-know Authority (The Contoso's STS in this case) and creates an equivalent SAML token signed by him.
3. The Fabrikam's services only accept SAML tokens signed by Fabrikam's STS.


With this solution, Fabrikam can start to accept request from other customers and the services will not notice the difference.
In addition, if the communication between the STS and the services in Fabrikam is protected by means of a Kerberos token instead of a SAML token, the
X509 certificates must be only deployed in the servers where both STS run.


Implementation


In order to implement this sample scenario, I used the SAML implementation for WSE. You can download the code for this project in the GDN workspace.
The image above illustrates the turn-key scenario used to secure the communication between the different participants in the architecture.



1. WSE Policy file in the client application

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">

  <extensions>

    <extension name="saml" type="Microsoft.Practices.WSSP.WSE3.QuickStart.SamlAssertion.SamlPolicyAssertion, Microsoft.Practices.WSSP.WSE3.QuickStart.SamlAssertion"/>

  </extensions>


  <policy name="PurchaseGoods">

    <saml issuer="http://localhost/FabrikamSTS/SamlTokenIssuer.ashx" issuerPolicy="FabrikamSTS" establishSecurityContext="true" renewExpiredSecurityContext="true" requireDerivedKeys="true">

      <protection>

        <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />

      </protection>

    </saml>

  </policy>

  <policy name="FabrikamSTS">

    <saml issuer="http://localhost/ContosoSTS/SamlTokenIssuer.ashx" issuerPolicy="ContosoSTS" establishSecurityContext="false" renewExpiredSecurityContext="false" requireDerivedKeys="true">

      <protection>

        <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />

      </protection>

    </saml>

  </policy>

  <policy name="ContosoSTS">

    <usernameForCertificateSecurity establishSecurityContext="true" renewExpiredSecurityContext="true" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncryptAndEncryptSignature" requireDerivedKeys="true">

      <serviceToken>

        <x509 storeLocation="LocalMachine" storeName="My" findValue="CN=ContosoSTS" findType="FindBySubjectDistinguishedName" />

      </serviceToken>

      <protection>

        <request signatureOptions="IncludeSoapBody" encryptBody="true" />

        <response signatureOptions="IncludeSoapBody" encryptBody="true" />

        <fault signatureOptions="IncludeSoapBody" encryptBody="false" />

      </protection>

    </usernameForCertificateSecurity>

  </policy>

</policies>


The policy definition above contains three policies:


1. PurchaseGoods: This policy is used to secure the communication between the client and the Fabrikam's service (PurchaseGoods service). As you can see, this service requires a SAML token created by the Fabrikam's STS. The address for that STS is specified in the attribute "issuer".

2. FabrikamSTS: This policy is used to secure the communication between the client and the STS in the Fabrikam realm. The Fabrikam's STS requires a SAML token from Contoso 

3. ContosoSTS: This policy is used to secure the communication between the client and the STS in the Contoso realm. In this case, to make the things simpler, I decided to use a UsernameForCertificate turn-key scenario although any of the available turn-key scenarios can be used here.


2. WSE Policy file in the Contoso's STS


<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">

  <policy name="ContosoSTS">

    <usernameForCertificateSecurity establishSecurityContext="true" renewExpiredSecurityContext="true" requireSignatureConfirmation="false"   messageProtectionOrder="SignBeforeEncryptAndEncryptSignature" requireDerivedKeys="true">

      <serviceToken>

        <x509 storeLocation="LocalMachine" storeName="My" findValue="CN=ContosoSTS" findType="FindBySubjectDistinguishedName" />

      </serviceToken>

      <protection>

        <request signatureOptions="IncludeSoapBody" encryptBody="true" />

        <response signatureOptions="IncludeSoapBody" encryptBody="true" />

        <fault signatureOptions="IncludeSoapBody" encryptBody="false" />

      </protection>

    </usernameForCertificateSecurity>

  </policy>

</policies>


3. SAML configuration for the Contoso's STS


<WseSaml>

  <samlTokenIssuer allowCachingToken="true" ttlInSeconds="300">

  <!-- the config for the saml token issuer, this is the only config we use. This token is used to sign the SAML token -->

    <serviceTokens>

      <!-- SAML Authority certificate -->

      <add uri="http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue/SAML" storeLocation="LocalMachine" storeName="My" findValue="CN=ContosoSTS" findType="FindBySubjectDistinguishedName" />

      <!-- Fabrikam STS -->

      <add uri="http://localhost/FabrikamSTS/SamlTokenIssuer.ashx" storeLocation="LocalMachine" storeName="My" findValue="CN=FabrikamSTS" findType="FindBySubjectDistinguishedName" />

    </serviceTokens>

    <policy name="ContosoSTS">

    </policy>

  </samlTokenIssuer>

</WseSaml>


A quick description of the configuration above:


a. allowCachingToken attribute: This attribute specifies whether the SAML token can be stored in a cache or not. Depending on this attribute, the STS will add or not a DoNotCacheCondition to the SAML token.

b. ttlInSeconds attribute: Lifetime in seconds for the SAML token

c. serviceTokens element: It specifies the location for the different X509 certificates required by the STS. The "SAML authority certificate" is used to sign the SAML token. (This signature is really important since the target service can trust or not in the SAML token depending on the certificate used to create it). The "Fabrikam STS certificate" is used to encrypt some sections in the SAML token, and thefore only the Fabrikam STS can decrypt those sections and use the token. In other words, it defines the X509 certificate for the target service.

d. policy element: The WSE policy used to secure the communication between the clients and the STS.


4. WSE policy file in the Fabrikam's STS


<policies>

  <extensions>

    <extension name="saml" type="Microsoft.Practices.WSSP.WSE3.QuickStart.SamlAssertion.SamlPolicyAssertion, Microsoft.Practices.WSSP.WSE3.QuickStart.SamlAssertion"/>

  </extensions>

  <policy name="FabrikamSTS">

    <saml issuer="http://localhost/ContosoSTS/SamlTokenIssuer.ashx" issuerPolicy="issuerPolicy" establishSecurityContext="false" renewExpiredSecurityContext="false" requireDerivedKeys="true">

      <trustedTokenIssuers>

        <add>

          <x509 storeLocation="LocalMachine" storeName="My" findValue="CN=ContosoSTS" findType="FindBySubjectDistinguishedName" />

        </add>

      </trustedTokenIssuers>

      <protection>

        <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />

      </protection>

    </saml>

  </policy>

</policies>


This STS only accepts SAML tokens created by the Contoso's STS (trustedTokenIssuers element in the policy definition). In other words, it only accepts SAML tokens signed by any of the issuers specified in the "trustedTokenIssuers" element.


5. SAML configuration for the Fabrikam's STS


<WseSaml>

  <samlTokenIssuer allowCachingToken="true" ttlInSeconds="300" >

  <!-- the config for the saml token issuer, this is the only config we use. This token is used to sign the SAML token -->

  <serviceTokens>

    <!-- SAML Authority certificate. Certificate used to sign the token -->

    <add uri="http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue/SAML"

storeLocation="LocalMachine" storeName="My" findValue="CN=FabrikamSTS" findType="FindBySubjectDistinguishedName"/>

    <!-- Fabrikam service. Certificate used to encrypt the token subject -->

    <add uri="http://localhost/FabrikamServices/PurchaseGoods.asmx"

storeLocation="LocalMachine" storeName="My" findValue="CN=FabrikamServices" findType="FindBySubjectDistinguishedName"/>

  </serviceTokens>

  <policy name="FabrikamSTS"></policy>

  </samlTokenIssuer>

</WseSaml>


As you can see, this configuration is similar to the configuration in the Contoso STS. The only difference is the configuration for the service tokens.


6. WSE policy file in the Fabrikam's service


<policies>

  <extensions>

    <extension name="saml" type="Microsoft.Practices.WSSP.WSE3.QuickStart.SamlAssertion.SamlPolicyAssertion, Microsoft.Practices.WSSP.WSE3.QuickStart.SamlAssertion"/>

  </extensions>

  <policy name="FabrikamServices">

    <saml issuer="http://localhost/FabrikamSTS/SamlTokenIssuer.ashx" issuerPolicy="issuerPolicy" establishSecurityContext="true" renewExpiredSecurityContext="true" requireDerivedKeys="true">

      <trustedTokenIssuers>

        <add>

          <x509 storeLocation="LocalMachine" storeName="My" findValue="CN=FabrikamSTS" findType="FindBySubjectDistinguishedName" />

        </add>

      </trustedTokenIssuers>

      <protection>

        <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="true" />

        <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />

      </protection>

    </saml>

   </policy>

</policies>


Again, it is similar to the policy in the Fabrikam's STS but the configuration for the "trustedTokenIssuers" element changes.


Send me an email if are interested in the source code of this sample. 

Comments

No Comments