Invoking WSE 3.0 Secure Web Services from Windows Workflow Foundation

I was recently asked if there was an easy way to invoke WSE 3.0 Web Services from a Windows Workflow Foundation (WF) workflow.  One of the key features that make WF different from other Workflow engines is the combination of declarative language (XAML) and imperative language (C#-VB.NET).  Having the capability to extend the workflow using .NET code makes it possible to invoke WSE 3.0 Web Services in the same way as a classic .NET client application.  The key is to apply the client policy to the proxy class used by the InvokeWebService activity.

 

WF provide the InvokeWebService activity that allows invoking operations in HTTP-hosted Web Services as a default; however this activity does not provide any out of the box support for WS-* protocols.  Using the ProxyClass and UserData properties of the InvokeWebService activity we can implement a generic technique in order to invoke WSE 3.0 Web Services.  The ProxyClass property contains the proxy type used to communicate with the Web Service.  The UserData property contains an IDictionary with data that can be used to extend/customize the functionality of the InvokeWebService activity.

Let’s view the following Web Service example step by step.

Step One: Web Service

 

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[Policy("servicePolicy")]

public class MathService : System.Web.Services.WebService

{

    public MathService () {

 

        //Uncomment the following line if using designed components

        //InitializeComponent();

    }

 

    [WebMethod]

    public int Add(int param1, int param2)

    {

        return param1 + param2;

    }

}


Step two.  The “servicePolicy” policy that implements an Anonymous over certificate scenario.

 

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

  <extensions>

    <extension name="anonymousForCertificateSecurity" type="Microsoft.Web.Services3.Design.AnonymousForCertificateAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

    <extension name="x509" type="Microsoft.Web.Services3.Design.X509TokenProvider, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

    <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

  </extensions>

  <policy name="servicePolicy">

    <anonymousForCertificateSecurity establishSecurityContext="false" renewExpiredSecurityContext="true" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="true" ttlInSeconds="300">

      <serviceToken>

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

      </serviceToken>

      <protection>

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

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

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

      </protection>

    </anonymousForCertificateSecurity>

    <requireActionHeader />

  </policy>

</policies>

 

Step three.  How to invoke this Web Service from WF we need to change the base type of the reference proxy class to Microsoft.Web.Services3.WebServiceClientProtocol.

[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")]

    [System.Diagnostics.DebuggerStepThroughAttribute()]

    [System.ComponentModel.DesignerCategoryAttribute("code")]

    [System.Web.Services.WebServiceBindingAttribute(Name = "ServiceSoap", Namespace = "http://tempuri.org/")]

    public partial class Service : Microsoft.Web.Services3.WebServicesClientProtocol

{

  Code here…

}

 

Step four.  We need to configure the client policy required to interface with the Web Service.

 

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

  <extensions>

    <extension name="anonymousForCertificateSecurity" type="Microsoft.Web.Services3.Design.AnonymousForCertificateAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

    <extension name="x509" type="Microsoft.Web.Services3.Design.X509TokenProvider, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

    <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

  </extensions>

  <policy name="clientPolicy">

    <anonymousForCertificateSecurity establishSecurityContext="false" renewExpiredSecurityContext="true" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="true" ttlInSeconds="300">

      <serviceToken>

        <x509 storeLocation="CurrentUser" storeName="AddressBook" findValue="CN=tc2003s" findType="FindBySubjectDistinguishedName" />

      </serviceToken>

      <protection>

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

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

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

      </protection>

    </anonymousForCertificateSecurity>

    <requireActionHeader />

  </policy>

</policies>

 

Step five.  Using the UserData property we set the name of the client policy that needs to be applied to the proxy.  We are using a CodeActivity executed before the InvokeWebService activity.

 

  private void codeActivity1_ExecuteCode(object sender, EventArgs e)

        {

            invokeWebServiceActivity1.UserData.Add("WSPolicy", "clientPolicy");

           

        }

 

Step six.  Our final step is to use the InvokingEvent of the InvokeWebService activity to set the client policy to the Web Service proxy.

 

private void invokeWebServiceActivity1_Invoking(object sender, InvokeWebServiceEventArgs e)

 {

 string policy= (string)((InvokeWebServiceActivity)sender).UserData["WSPolicy"];

         ((WebServicesClientProtocol)e.WebServiceProxy).SetPolicy(policy);

}

 

This generic technique can be applied to different turnkey security scenarios implemented in WSE 3.0.  The creation of custom WF activities may be required for more complex interactions (like multi-transport).  The code for this example is available at the WF community site.

1 Comment

Comments have been disabled for this content.